- IOC
IOC,控制反转(Inversion of Control),就是把对象的创建(即bean的new操作),交给Spring来实现。
通过XML配置:bean标签是用于配置被spring容器管理的bean信息,我们可以通过bean标签,完成IOC的配置。
- 使用默认无参构造函数来创建类对象,并存入spring容器
<bean id="userService" class="com.xcj.spring.service.UserServiceImpl"></bean>
public class UserServiceImpl implements UserService { }
- 静态工厂方式
<!-- 使用StaticFactory类中的静态方法createDemoService创建对象,并存入spring容器(简单了解下,实际中基本不用) --> <bean id="demoService" class="com.xcj.spring.factory.StaticFactory" factory-method="createDemoService"></bean>
public class StaticFactory { public static DemoService createDemoService() { return new DemoServiceImpl(); } }
public class DemoServiceImpl implements DemoService { }
- 实例工厂方式
<!-- 先把工厂的创建交给spring来管理,再在使用工厂的bean来调用里面的方法 --> <bean id="instanceFactory" class="com.xcj.spring.factory.InstanceFactory"></bean> <bean id="beanService" factory-bean="instanceFactory" factory-method="createBeanService"></bean>
public class InstanceFactory { public static BeanService createBeanService() { return new BeanServiceImpl(); } }
public class BeanServiceImpl implements BeanService { }
DI,依赖注入(Dependency Injection),是spring IOC的具体实现,做的工作是Spring实例化bean对象后,对bean对象的属性信息进行赋值的操作。
依赖指的是bean实例中的属性,分为简单类型(8种基本类型和String类型)的属性、POJO类型的属性、集合数组类型的属性。
注入方式:构造器,set方法,注解,(还有使用p名称空间注入数据,本质上还是调用set方法)。
- 构造器注入
<bean id="userDemoService" class="com.xcj.spring.service.UserDemoServiceImpl"> <constructor-arg name="id" value="1"></constructor-arg> <constructor-arg name="name" value="zhangsan"></constructor-arg> </bean>
public class UserDemoServiceImpl implements UserDemoService { private int id; private String name; public UserDemoServiceImpl(int id, String name) { this.id = id; this.name = name; } }
- set方法注入(手动装配,即XML方式)
<bean id="demoSetService" class="com.xcj.spring.service.DemoSetServiceImpl"> <!-- List类型的属性 --> <property name="lists"> <list> <value>11</value> <value>22</value> </list> </property> <!-- Set类型的属性 --> <property name="sets"> <set> <value>111</value> <value>222</value> </set> </property> <!-- Map类型的属性 --> <property name="maps"> <map> <entry key="111" value="111-01"/> <entry key="222" value="222-01"/> </map> </property> <!-- Properties类型 --> <property name="props"> <props> <prop key="name">root</prop> <prop key="password">123456</prop> </props> </property> </bean>
public class DemoSetServiceImpl implements DemoSetService { private List<Object> lists; private Set<Object> sets; private Map<String, Object> maps; private Properties props; public void setLists(List<Object> lists) { this.lists = lists; } public void setSets(Set<Object> sets) { this.sets = sets; } public void setMaps(Map<String, Object> maps) { this.maps = maps; } public void setProps(Properties props) { this.props = props; } }
- 注解(自动装配)
(1)@Autowired,按类型注入,要唯一;如果遇到相同的,则要加上@Qualifer,表示在类型注入基础上按名称注入。
(2)@Resource,按照bean的name/id注入。
@Service("userService") public class UserServiceImpl implements UserSetService{ @Autowired private UserDao userDao; }
那么问题:Spring怎么解决循环依赖的?通过三级缓存。
循环依赖:两个或多个类之间,形成的互相引用,就是循环依赖。
- 通过构造方法的循环依赖
构造器的循环依赖,是在bean的实例化new对象时产生的,这里会发生死锁,构造器循环依赖无法解决,只能避免这种方式,尽量在设计时不出现循环依赖的问题,或者改成setter方法。
- 通过setter方法的循环依赖
setter方法的循环依赖,是在bean的实例化后,对bean属性进行填充时出现。这里需要说明,Spring中Bean需要初始化完成,才能对外使用,创建bean的过程包括实例化、属性填充、初始化、放入singletonObjects集合。
Spring解决setter方法的循环依赖是通过三级缓存解决:
查找单例bean顺序singletonObjects -> earlySingletonObjects -> singletonFactories:
先从一级缓存singletonObjects查找,如果没有且在创建中,则在二级缓存earlySingletonObjects中查找,还是找不到,且允许循环引用,则进入三级缓存singletonFactories中查找,找到会将其插入二级缓存earlySingletonObjects中,同时将其从三级缓存删除。
添加Bean顺序:
(1)new实例化后,如果一级缓存没有,则bean会存放在三级缓存singletonFactories中,并尝试删除二级缓存中可能存在的bean实例;
(2)new实例化后,如果一级和二级缓存都没有,则在三级缓存中找,找到后会放入二级缓存,并删除三级缓存存放的bean实例或代理对象。
(3)如果bean创建完成,则会直接存放在一级缓存singletonFactories,同时删除二级缓存和三级缓存。
- singletonObjects:存放完整的Bean实例。
- earlySingletonObjects:存放半成品Bean实例。
- singletonFactories:存放实例对象工厂ObjectFactory。
- AOP
AOP,面向切面编程(Aspect Oriented Programming),是一种编程范式,是OOP(面向对象编程)的延续,采用横向抽取机制,补充了OOP纵向继承体系无法解决的重复代码优化方式。
譬如,假设有A,B有相同的逻辑,我们可以横向抽取相同的部分出来,通过AOP思想和业务串联起来,同时这是可插拔式的。
AOP思想的实现一般都是基于代理模式,给业务代码进行功能增强,将业务代码和系统代码解耦。
如果目标对象的实现类实现了接口,Spring AOP 将会采用 JDK 动态代理来生成 AOP 代理类;
如果目标对象的实现类没有实现接口,Spring AOP 将会采用 CGLIB 来生成 AOP 代理类。
动态代理说明,动态代理是在运行期间,针对目标对象进行动态代理。
关于这里涉及到的代理模式,可以看https://www.cnblogs.com/scorpio-cat/p/12715222.html。
(1)使用XML方式实现
public class AopAdvice { public void saveLog() { System.out.println("保存日志"); } }
<bean name="aopAdvice" class="com.xcj.spring.advice.AopAdvice"></bean> <aop:config> <aop:aspect ref="aopAdvice"> <aop:before method="saveLog" pointcut="execution(* com.xcj.spring..*Service.do*(..))" /> </aop:aspect> </aop:config>
(2)使用注解实现
@Component("aopDemoAdvice") @Aspect public class AopDemoAdvice { @Before(value="execution(* com.xcj.spring..*Service.do*(..))") public void saveLog() { System.out.println("保存日志"); } }
<!-- Spring AOP,通过动态代理技术实现的,而动态代理是基于反射 --> <!-- 开启注解并扫描指定包中带有注解的类 --> <context:component-scan base-package="com.xcj.spring.service"></context:component-scan> <!-- 开启AOP自动代理 --> <aop:aspectj-autoproxy />
使用纯注解方式,@EnableAspectJAutoproxy代替 <aop:aspectj-autoproxy /> ,@Component-scan代替 <context:component-scan>
@Configuration @ComponentScan(basePackages = "com.xcj.spring.service") @EnableAspectJAutoProxy public class SpringAOPConfiguration { }
Spring帮我们实现了事务管理的系统代码,我们可以通过配置,将业务代码与事务管理整合在一起。当然,我们也可以自己编写事务管理的代码,然后通过AOP思想组合在一起。
Spring AOP的事务支持:Spring提供了事务管理接口PlatformTransactionManager,JDBC、MyBatis等根据这个接口实现了对应的事务管理器,如DataSourceTransactionManager。
(1)XML方式
public void doSave(User user) { userDao.save(user); LogInfo logInfo = new LogInfo(); logInfo.setContent("新增User:"+user.getName); logDao.saveLog(logInfo); }
<!-- 事务管理器 --> <bean name="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource" /> </bean> <!-- 事务通知,配置事务相关属性:方法、隔离级别、传播行为 --> <tx:advice id="txAdvice" transaction-manager="transactionManager"> <tx:attributes> <tx:method name="find*" read-only="true" /> <tx:method name="get*" read-only="true" propagation="SUPPORTS" /> <tx:method name="check*" read-only="true" propagation="SUPPORTS" /> <tx:method name="delete*" propagation="REQUIRED"/> <tx:method name="do*" propagation="REQUIRED" /> <tx:method name="submit" propagation="REQUIRED"/> <tx:method name="create" propagation="REQUIRED"/> <tx:method name="ido*" propagation="REQUIRES_NEW" /> <!-- 必须在事务中运行 --> </tx:attributes> </tx:advice> <!-- AOP配置 --> <aop:config proxy-target-class="true"> <aop:pointcut id="txPointCut" expression="execution(* com.xcj.spring..*Service.*(..))" /> <aop:advisor id="serviceTx" pointcut-ref="txPointCut" advice-ref="txAdvice" order="1" /> </aop:config>
(2)注解方式,XML配置开启事务注解,注解@Transaction。如需使用纯注解,使用@EnableTransactionManagement
<!-- 配置事务管理器 --> <tx:annotation-driven transaction-manager="transactionManager" /> <bean name="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource" /> </bean>
关于AOP的一些问题:
1.什么时候产生代理对象?
IOC容器在创建Bean对象的时候,会触发AOP,产生代理对象并放入IOC容器中。
2.JDK代理方式中,什么时候调用代理对象?
第一次调用目标类接口的实例方法的时候,就会去调用代理对象。
3.有没有可能产生了代理对象,但从没有调用过?
可能。
4.JDK代理方式中,代理对象的处理逻辑是什么?
InvocationHandler的invoke()方法,就是组织增强代码和目标代码的结合。
5.Spring 用到了哪些设计模式?
- 简单工厂模式(BeanFactory、
ApplicationContext
) - 工厂方法模式(将对象的创建及初始化职责交给工厂对象)
- 单例模式(单例对象,提供了全局的访问点BeanFactory)
- 适配器模式(AdvisorAdapter,AOP用到的,
BeforeAdvice
、AfterAdvice、AfterReturningAdvice,Spring预定义的通知要通过对应的适配器,适配成MethodInterceptor
接口(方法拦截器)类型的对象(如:MethodBeforeAdviceInterceptor
负责适配MethodBeforeAdvice
)) - 装饰者模式(数据库连接sessionFactory,dataSource)
- 代理模式(AOP用到的)
- 策略模式(SimpleInstantiationStrategy,实例化对象的时候用到的)
- 观察者模式(Spring 事件驱动模型:
ApplicationEvent
事件、ApplicationListener
事件监听者、ApplicationEventPublisher
事件发布者) - 模板方法模式(JdbcTemplate、hibernateTemplate)