五、AOP:【动态代理】
指在程序运行期间动态的将某段代码切入到指定方法指定位置进行运行的编程方式
-
1、导入aop模块:Spring AOP :(spring-aop)
org.springframework spring-aop 4.3.12.RELEASE -
2.定义一个业务逻辑类(MathCalculator)在业务逻辑运行的时候将日志进行打印(方法之前、方法运行结束、方法出现异常,XXX)
public class MathCalculator { public int div(int i,int j){ System.out.println("MathCalculator......div"); return i/j; } }
-
3.定义一个日志切面类(LogAspects):切面类里面的方法需要动态感知MathCalculator.div运行
/** * 切面类 */ @Aspect public class LogAspects { //抽取公共的切入点表达式 @Pointcut("execution(public int com.it.aop.MathCalculator.*(..))") public void pointCut(){}; @Before(value = "pointCut()") public void logStart(){ System.out.println("除法运行。。。。参数列表是:{result}"); } @After("execution(public int com.it.aop.MathCalculator.*(..))") public void logEnd(JoinPoint joinPoint){ System.out.println(joinPoint.getSignature().getName()+"除法结束。。。。@After"); } //@Before在目标之前切入,切入点表达式(指定在整个切入点) @AfterReturning(value = "pointCut()",returning = "result") public void logReturn(Object result){ System.out.println("除法正常返回。。。。运行结果:{"+result+"}"); } //注意:JoinPoint必须写在第一个参数列表上,否则就会出错 @AfterThrowing(value = "pointCut()",throwing = "exception") public void logException(JoinPoint joinPoint,Exception exception){ System.out.println(joinPoint.getSignature().getName()+"除法异常。。。。异常信息:{"+exception+"}"); } }
-
通知方法:
前置通知(@Before):logStart:在目标方法(div)运行之前运行
后置通知(@After):logEnd:在目标方法(div)运行结束之后运行(无论方法正常结束还是异常结束)
返回通知(@AfterReturning):logReturn:在目标方法(div)正常返回之后运行
异常通知(@AfterThrowing:logException:在目标方法(div)出现异常之后运行
环绕通知(@Around): 动态代理,手动推荐目标方法运行(joinPoint.proced())
4.给切面类的目标方法标注何时何地运行(通知注解)
-
5.将切面类和业务逻辑类(目标方法所有类)都加入到容器中
@EnableAspectJAutoProxy @Configuration public class MainConfigOfAOP { //业务逻辑类加入到容器中 @Bean public MathCalculator calculator(){ return new MathCalculator(); } //切面类加入到容器中 @Bean public LogAspects logAspects(){ return new LogAspects(); } }
6.必须告诉spring哪个类是切面类(给切面类加上一个注解@Aspect)
-
[7].给配置类中加入@EnableAspectJAutoProxy 【开启基于注解的aop模式】
-
xml配置:
-
注解方式:
@EnableAspectJAutoProxy @Configuration public class MainConfigOfAOP { ..... }
-
-
8、测试
@Test public void test01(){ //1.创建applicationContext AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfigOfAOP.class); //1.不要自己创建对象,使用spring容器中的组件 MathCalculator mathCalculator = applicationContext.getBean(MathCalculator.class); mathCalculator.div(1,0); applicationContext.close(); }
结果。
1.AOP原理:
【看给容器中注册了什么组件,这个组件什么时候工作,这个组件的功能是什么?】
1、@EnableAspectJAutoProxy
@Import(AspectJAutoProxyRegistrar.class):给容器中导入AspectJAutoProxyRegistrar
利用AspectJAutoProxyRegistrar自定义给容器中注册bean:BeanDefinetion
internalAutoProxyCreator=AnnotationAwareAspectJAutoProxyCreator
给容器中注册一个AnnotationAwareAspectJAutoProxyCreator;
2、AnnotationAwareAspectJAutoProxyCreator:
AnnotationAwareAspectJAutoProxyCreator:
->AspectJAwareAdvisorAutoProxyCreator
->AbstractAdvisorAutoProxyCreator
->AbstractAutoProxyCreator
implements SmartInstantiationAwareBeanPostProcessor, BeanFactoryAware
关注后置处理器(在bean初始化完成 前后做事情)、自动装配BeanFactoryAware
AbstractAutoProxyCreator.setBeanFactory()
AbstractAutoProxyCreator.有后置处理器的逻辑;
AbstractAdvisorAutoProxyCreator.setBeanFactory()-》initBeanFactory()
AnnotationAwareAspectJAutoProxyCreator.initBeanFactory()
流程:
1、传入配置类创建ioc容器
2、注册配置类,调用refresh()刷新容器
-
3、registerBeanPostProcessors(beanFactory);注册bean的后置处理器,方便拦截bean的创建
1)、先获取ioc容器已经定义了的需要创建对象的所有的BeanPostProcessor
2)、给容器中加别的BeanPostProcessor
3)、优先注册实现了PriorityOrder接口的BeanPostProcessor;
4)、再给容器中注册实现了Ordered接口的BeanPostProcessor;
5)、注册没实现优先级接口的BeanPostProcessor:
-
6)、注册BeanPostProcessor,实际上就是创建BeanPostProcessor对象,保存在容器中;
-
创建internalAutoProxyCreator的BeanPostProcessor【AnnotationAwareAspectJAutoProxyCreator】
1)、创建Bean实例
2)、populateBean:给bean的各种属性赋值
3)、initializeBean:初始化bean
1)、invokeAwareMethods:处理Aware接口的方法回调 2)、applyBeanPostProcessorsBeforeInitialization():应用后置处理器的 PostProcessorsBeforeInitialization(); 3)、invokeInitMethods():执行自定义的初始化方法 4)、applyBeanPostProcessorsAfterInitialization():执行后置处理器的 PostProcessAfterInitialization();
4)、 BeanPostProcessor(AnnotationAwareAspectJAutoProxyCreator)创建 成功--》aspectJAdvisorsBuilder;
7)、把BeanPostProcessor注册到BeanFactory中
beanFactory.addBeanPostProcessor(postProcessor);
-
=====================以上是创建和注册AnnotationAwareAspectJAutoProxyCreator的过程===
AnnotationAwareAspectJAutoProxyCreator => InstantiationAwareBeanPostProcessor
-
4、finishBeanFactoryInitialization(beanFactory);完成BeanFactory初始化工作;创建剩下的单实例bean
1)、遍历获取容器中所有的Bean,依次创建对象getBean(beanName);
getBean->doGetBean()->getSingleton()-> 2)、创建bean 【AnnotationAwareAspectJAutoProxyCreator在所有bean创建之前会有一个拦截, InstantiationAwareBeanPostProcessor,会调用postProcessBeforeInstantiation()】 1)、先从缓存中获取当前bean,如果能获取到,说明bean是之前被创建过的,直接 使用,否则再创建; 只要创建好的Bean都会被缓存起来 2)、createBean();创建bean; AnnotationAwareAspectJAutoProxyCreator 会在任何bean创建之前先尝试返回 bean的实例 【BeanPostProcessor是在Bean对象创建完成初始化前后调用的】 【InstantiationAwareBeanPostProcessor是在创建Bean实例之前先尝试用后置处理 器返回对象的】 **注:InstantiationAwareBeanPostProcessor是BeanPostProcessor的子接口** 1)、resolveBeforeInstantiation(beanName, mbdToUse);解析 BeforeInstantiation 希望后置处理器在此能返回一个代理对象;如果能返回代理对象就使用,如果不能就 继续 1)、后置处理器先尝试返回对象; bean = applyBeanPostProcessorsBeforeInstantiation(): 拿到所有后置处理器,如果是InstantiationAwareBeanPostProcessor; 就执行postProcessBeforeInstantiation if (bean != null) { bean = applyBeanPostProcessorsAfterInitialization(bean, beanName); } 2)、doCreateBean(beanName, mbdToUse, args);真正的去创建一个bean实 例;和3.6流程一样; 3)、
AnnotationAwareAspectJAutoProxyCreator【InstantiationAwareBeanPostProcessor】的作用:
1)、每一个bean创建之前,调用postProcessBeforeInstantiation();
关心MathCalculator和LogAspect的创建
1)、判断当前bean是否在advisedBeans中(保存了所有需要增强bean)
2)、判断当前bean是否是基础类型的Advice、Pointcut、Advisor、AopInfrastructureBean,
或者是否是切面(@Aspect)
3)、是否需要跳过
1)、获取候选的增强器(切面里面的通知方法)【ListcandidateAdvisors】
每一个封装的通知方法的增强器是 InstantiationModelAwarePointcutAdvisor;
判断每一个增强器是否是 AspectJPointcutAdvisor 类型的;返回true
2)、永远返回false2)、创建对象
postProcessAfterInitialization;
return wrapIfNecessary(bean, beanName, cacheKey);//包装如果需要的情况下
1)、获取当前bean的所有增强器(通知方法) Object[] specificInterceptors
1、找到候选的所有的增强器(找哪些通知方法是需要切入当前bean方法的)
2、获取到能在bean使用的增强器。
3、给增强器排序
2)、保存当前bean在advisedBeans中;
3)、如果当前bean需要增强,创建当前bean的代理对象;
1)、获取所有增强器(通知方法)
2)、保存到proxyFactory
3)、创建代理对象:Spring自动决定
JdkDynamicAopProxy(config);jdk动态代理;
ObjenesisCglibAopProxy(config);cglib的动态代理;
4)、给容器中返回当前组件使用cglib增强了的代理对象;
5)、以后容器中获取到的就是这个组件的代理对象,执行目标方法的时候,代理对象就会执行通知方法的流程;
3)、目标方法执行 ;
容器中保存了组件的代理对象(cglib增强后的对象),这个对象里面保存了详细信息(比如增强器,目标对象,xxx);
1)、CglibAopProxy.intercept();拦截目标方法的执行
2)、根据ProxyFactory对象获取将要执行的目标方法拦截器链;
List
总结:
1)、使用@EnableAspectJAutoProxy 开启AOP功能
2)、@EnableAspectJAutoProxy 会给容器中注册一个组件 AnnotationAwareAspectJAutoProxyCreator
3)、AnnotationAwareAspectJAutoProxyCreator是一个后置处理器
4)、容器的创建流程:
1)、registerBeanPostProcessors()注册后置处理器;创建AnnotationAwareAspectJAutoProxyCreator对象
2)、finishBeanFactoryInitialization()初始化剩下的单实例bean
1)、创建业务逻辑组件 和切面组件
2)、AnnotationAwareAspectJAutoProxyCreator拦截组件的创建过程
3)、组件创建完成之后,判断组件是否需要增强
是:切面的通知方法,包装成增强器(Advisor);给业务逻辑创建一个代理对象:默认为 cglib。
5)、执行目标方法:
1)、代理对象要执行目标方法
2)、CglibAopProxy.intercept();
1)、得到目标方法的拦截器(增强器包装成拦截器MethodInterceptor)
2)、利用拦截器的链式机制,依次进入每一个拦截器执行:
3)、效果:
正常执行:前置通知 ------》目标方法--》后置通知 --》返回通知
出现异常:前置通知 ------》目标方法--》后置通知 --》异常通知
六、声明事务
1.基于注解方式做事务处理
环境搭建:
1).导入相关依赖
* 数据源,数据库驱动、Spring-jdbc模块
org.springframework
spring-jdbc
4.3.12.RELEASE
c3p0
c3p0
0.9.1.2
org.springframework
spring-tx
4.3.12.RELEASE
2).配置数据源、JdbcTemplate(Spring提供简化数据源操作)操作数据源
@EnableTransactionManagement
@ComponentScan("com.it.tx")
@Configuration
public class TxConfig {
//数据源
@Bean
public DataSource dataSource() throws Exception{
ComboPooledDataSource dataSource = new ComboPooledDataSource();
dataSource.setUser("root");
dataSource.setPassword("");
dataSource.setDriverClass("com.mysql.jdbc.Driver");
dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/test");
return dataSource;
}
//
@Bean
public JdbcTemplate jdbcTemplate() throws Exception{
//spring对@Configuration会特殊处理,给容器中加组件的方法多次调用都只是从容器中找组件
JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource());
return jdbcTemplate;
}
//注册事务管理器
@Bean
public PlatformTransactionManager platformTransactionManager() throws Exception{
return new DataSourceTransactionManager(dataSource());
}
}
3).给方法标注上@Transactional 表示当前方法是一个事务方法
4).@EnableTransactionManagement开启基于注解的事务管理功能:
5).配置事务管理器来控制事务
- dao
@Repository
public class UserDao {
@Autowired
private JdbcTemplate jdbcTemplate;
public void insert(){
String sql = "INSERT INTO `tbl_user`(username,age) VALUES(?,?);";
String username = UUID.randomUUID().toString().substring(0, 5);
jdbcTemplate.update(sql,username,19);
}
}
- service
@Service
public class UserService {
@Autowired
private UserDao userDao;
@Transactional//不加事务,数据库就不会回滚,会添加一条记录
public void insertUser(){
userDao.insert();
//otherDao.other();
System.out.println("插入完成.....");
int i = 10/0;
}
}
- 测试
@Test
public void test01(){
//1.创建applicationContext
AnnotationConfigApplicationContext applicationContext =
new AnnotationConfigApplicationContext(TxConfig.class);
UserService userService = applicationContext.getBean(UserService.class);
userService.insertUser();
applicationContext.close();
}
- 效果
此时的数据库中并未添加新的记录,说明 我们的事务管理开始工作了。
2.@EnableTransactionManagement的实现原理:
1)、@EnableTransactionManagement
利用TransactionManagementConfigurationSelector给容器中导入组件。
导入两个组件
AutoProxyRegistrar、
ProxyTransactionManagementConfiguration
2)、AutoProxyRegistrar:
给容器中注册一个InfrastructureAdvisorAutoProxyCreator组件
InfrastructureAdvisorAutoProxyCreator:?
利用重置处理器机制在对象创建以后,包装对象,返回一个代理对象(增强器),代理对象执行方法利用拦截器链进行调用;
3)、ProxyTransactionManagementConfiguration 做了什么?
1.给容器中注册事务增强器:
事务增强器要用事务注解 的信息,AnnotationTransactionAttributeSource来解析事务注解
2.事务拦截器:
TransactionInterceptor:保存了事务属性的信息,事务管理器;
他是一个MethodInterceptor:
在目标方法执行的时候:
执行拦截器链:
事务拦截器:
1)、先获取事务相关的属性
2)、再获取PlatformTransactionManager,如果事先没有添加指定任何
transactionmanager,最终会从容器中执照类型获取一个PlatformTransactionManager
3)、执行目标方法
如果异常,获取到事务管理器,利用事务管理器回滚操作:
如果正常,利用事务管理器,提交 事务
- 事务抽象
- 事务拦截
3.BeanFactoryPostProcessor:
扩展原理:
BeanPostProcessor:bean后置处理器,bean创建对象初始化前后进行拦截工作的
BeanFactoryPostProcessor:beanFactory的后置处理器:
在BeanFactory标准初始化之后调用:所有的bean定义已经保存加载
到BeanFactory但bean的实例还未创建
1)、ioc容器创建对象
2)、invokeBeanFactoryPostProcessors(beanFactory);执行BeanFactoryPostProcessor;
如何找到所有BeanFactoryPostProcessor并执行他们的方法:
1)、直接在BeanFactory中找到所有类型是BeanFactoryPostProcessor的组件并执行 他们的方法
2)、在初始化创建其他组件前面执行
- configuration
@ComponentScan("com.it.ext")//包扫描
@Configuration
public class ExtConfig {
@Bean
public Blue blue(){
return new Blue();
}
}
- Blue
public class Blue {
public Blue(){
System.out.println("blue.......constructor");
}
public void init(){
System.out.println("blue...init...");
}
public void detory(){
System.out.println("blue...detory...");
}
}
- 组件
@Component
public class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
System.out.println("MyBeanFactoryPostProcessor.....postProcessBeanFactory");
int count = beanFactory.getBeanDefinitionCount();
String[] names = beanFactory.getBeanDefinitionNames();
System.out.println("当前BeanFactory中有"+count+"个bean");
System.out.println(Arrays.asList(names));
}
}
- Test
@Test
public void test01(){
//1.创建applicationContext
AnnotationConfigApplicationContext applicationContext =
new AnnotationConfigApplicationContext(ExtConfig.class);
applicationContext.close();
}
- 结果
4.BeanDefinitionRegistryPostProcessor :
BeanDefinitionRegistryPostProcessor extends BeanFactoryPostProcessor
postProcessBeanDefinitionRegistry();
在所有bean定义信息将要被加载,bean实例还未创建
BeanDefinitionRegistry Bean定义信息的保存中心,以后BeanFactory就是按照
BeanDefinitionRegistry 里面保存的每一个bean定义信息创建bean实例;
优先于BeanFactoryPostProcessor执行;
利用BeanDefinitionRegistryPostProcessor给容器中再额外添加一些组件;
原理:
1)、ioc创建对象
2)、refresh()方法---》invokeBeanFactoryPostProcessors(beanFactory);
3)、从容器中获取得到所有的BeanDefinitionRegistryPostProcessor组件。
1、依次触发所有的postProcessBeanDefinitionRegistry()方法。
2、再来触发postProcessBeanFactory()方法BeanFactoryPostProcessor;
4)、再来从容器中找到BeanFactoryPostProcessor组件;然后依次触发 postProcessBeanFactory()方法
结论:BeanDefinitionRegistryPostProcessor中的postProcessBeanDefinitionRegistry()优先于BeanFactoryPostProcessor中的postProcessBeanFactory()。
- 其他的代码如3所示,只是再添加一个组件
@Component
public class MyBeanDefinitionRegistryPostProcessor implements BeanDefinitionRegistryPostProcessor {
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
System.out.println("postProcessBeanDefinitionRegistry...bean的数量:"+registry.getBeanDefinitionCount());
// RootBeanDefinition beanDefinition = new RootBeanDefinition(Blue.class);
AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.rootBeanDefinition(Blue.class).getBeanDefinition();
//注册一个id为hello的组件
registry.registerBeanDefinition("hello",beanDefinition);
}
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
System.out.println("MyBeanDefinitionRegistryPostProcessor.....bean的数 量:"+beanFactory.getBeanDefinitionCount());
}
}
测试方法如图3所示
结果
监听容器中发布的事件,事件驱动模型开发。
public interface ApplicationListener
监听 ApplicationEvent 及其下面的子事件;
【自己发布事件的步骤:】
1)、写一个监听器(ApplicationListener)来监听某个事件(ApplicationEvent及其子类)
@EventListener;
原理:EventListenerMethodProcessor
2)、把监听器加入到容器;
3)、只要容器中有相应类型的事件发布,我们就能监听到这个事件
ContextRefreshedEvent:容器刷新完成(所有的bean都完全创建)会发布这个事件
ContextClosedEvent:关闭容器会发布这个事件;
4)、发布一个事件;
applicationContext.publishEvent();
原理:
ContextRefreshedEvent、IocTest_Ext$1[source=我发布了一个事件]、ContextClosedEvent
1)、ContextRefreshedEvent事件:
1)、容器创建对象:refresh();
2)、finishRefresh();容器刷新完成会发布ContextRefreshedEvent事件
2)、自己发布事件:
3)、容器关闭会发布:publishEvent(new ContextClosedEvent(this));
【事件发布流程】:
3)、publishEvent(new ContextRefreshedEvent(this));
1)、获取事件的多播器(派发器):getApplicationEventMulticaster()
2)、multicastEvent派发事件
3)、获取到所有的ApplicationListener;监听器
for (final ApplicationListener> listener : getApplicationListeners(event, type))
1)、如果有Executor,可以支持使用Executor进行异步派发;
Executor executor = getTaskExecutor();
2)、否则同步的方式直接执行listener方法: invokeListener(listener, event);拿到listener回调onApplicationEvent方法;
【事件多播器(派发器)】
1)、容器创建对象:refresh()
2)、initApplicationEventMulticaster();初始化initApplicationEventMulticaster
1)、先去容器中找有没有=“applicationEventMulticaster”的组件;
2)、如果没有this.applicationEventMulticaster = new SimpleApplicationEventMulticaster(beanFactory);
并且加入到容器中,我们就可以在其他组件要派发事件,自动注入applicationEventMulticaster;
【容器中有哪些监听器】
1)、容器创建对象:refresh()
2)、registerListeners();注册监听器applicationEventMulticaster中;
从容器中拿到所有的监听器,把他们注册到
String[] listenerBeanNames = getBeanNamesForType(ApplicationListener.class, true, false);
//将listener注册到ApplicationEventMulticaster中 getApplicationEventMulticaster().addApplicationListenerBean(listenerBeanName);