Spring-AOP:AOP + Aspects;AOP框架之一,其他比如还有AspectJ
Aspect-Oriented-Programming(面向切面编程),一种编程思想。
切面:Aspect,由切入点
和额外功能
组成。
作用:提供了新的编程角度,不再只是考虑类、对象
,而可以考虑切面
。切面和目标形成代理
,解决项目业务中额外功能冗余的问题。
实现方式:JDK动态代理;CGLIB动态代理
AOP使用
用aop最多的场景:事务控制;其他时候很少使用
- 导入依赖:
<!-- 1> 负责和AspectJ框架集成。Spring-AOP 和 AspectJ 都是AOP框架,一个面向spring工厂中的bean,一个可以面向所有java对象
2> aspectweaver 即 AspectJ 的库。
3> spring-AOP模块,并没有独立提供完整的AOP实现,而是集成了【AspectJ框架(AOP框架)】,比如所有的切入点表达式来自AspectJ
4> spring-aspects就是负责集成AspectJ。
-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
<version>4.3.6.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>4.3.6.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>4.3.6.RELEASE</version>
</dependency>
<!-- spring-aspects 会传递导入
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.9</version>
<scope>compile</scope>
</dependency>
而aspectweaver 即 AspectJ 的一个库,(可以github搜索“AspectJ”,查看在Github上的镜像)。
spring-AOP模块,并没有独立提供完整的AOP实现,而是集成了【AspectJ框架(AOP框架)】
spring-aspects就是负责集成AspectJ。
-->
<!-- AOP联盟,可省略,spring4.3.6中的 aop模块中添加了其中的api( 兼容了AOP联盟 )
<dependency>
<groupId>aopalliance</groupId>
<artifactId>aopalliance</artifactId>
<version>1.0</version>
</dependency>
-->
- 准备Target:
public class UserServiceImpl implements UserService{
private UserDAO userDAO;
@Override
public void updateUser(User user) {
System.out.println("update in service===============");
userDAO.updateUser(user);
}
@Override
public void insertUser(User user) {
System.out.println("insert in service===============");
userDAO.insertUser(user);
}
// set/get省略
}
- 准备Advice:
public class MyAdvice {//POJO 类中可以包含多个 额外功能,此处暂且定义一个
public void beforezhj() {//前置额外功能
System.out.println("before~~~~");//模拟前置的额外功能逻辑
}
}
- 编织Weave:
所谓编织,即,将Target 和 Advice 组装 形成代理。
当然组装过程由spring管理,开发者只需要做出配置,告知spring需要组装谁即可
<!-- 声明 Target + Advice -->
<!-- 声明 Target -->
<bean id="userService" class="com.zhj.service.UserServiceImpl">
<!-- 为userDAO属性赋值,值为id=“userDAO”的组件 -->
<property name="userDAO" ref="userDAO"/>
</bean>
<!-- Advice -->
<bean id="myAdvice" class="com.zhj.advice.MyAdvice"/>
<!-- 编织 配置 -->
<aop:config>
<!-- ref="引入MyAdvice" -->
<aop:aspect ref="myAdvice">
<!-- 切入点=pointcut
execution()表达式:描述切入位置
组成:修饰符 返回值 包 类 方法名 参数表
public Integer com.xx.xxx.AA.xxxXXX(int,String)
* com.service.UserServiceImpl.*(..):com.service包下UserServiceImpl类中,返回值修饰符任意,方法名任意,参数表任意
* com.service.UserServiceImpl.queryUser(..):同上,只是方法名不是任意,而是 ”queryUser“
-->
<aop:pointcut id="pc" expression="execution(* com.zhj.service.UserServiceImpl.*(..))"/>
<!-- aop:before 前置切入
method="MyAdvice类中的方法" pointcut-ref="beforezhj切入的位置" -->
<aop:before method="beforezhj" pointcut-ref="pc"/>
</aop:aspect>
</aop:config>
- 测试:
UserService userService = (UserService)context.getBean("userService");
userService.insertUser(new User(...));
Advice种类
- 前置额外功能:
public class MyAdvicee {
public void beforezhj() {
System.out.println("before~~~~");
}
}
- 后置额外功能:
public class MyAdvicee {
public void beforezhj() {...}
// 在核心功能执行并返回后,执行,ret9核心功能返回值,如果核心功能方法是void,则ret9=null
public void myAfterReturning(Object ret9){
System.out.println("after~~~~:"+ret9);
}
}
- 环绕额外功能:
public class MyAdvicee {
public void beforezhj() {...}
public void myAfterReturning(Object ret9){...}
/**
在核心功能前后 都执行
p.proceed();==> 调用核心功能,即控制了核心功能的执行,进而完成环绕效果
**/
public Object myInterceptor(ProceedingJoinPoint p) throws Throwable {
System.out.println("interceptor_pre~~~~");// 前
Object ret = p.proceed();//核心功能执行,注意要接收返回值
System.out.println("interceptor_post~~~~");// 后
return ret;//注意:要向上返回核心功能的返回值,否则,调用此代理的组件将得不到核心功能的返回值
}
}
- 异常额外功能:
public class MyAdvicee {
public void beforezhj() {...}
public void myAfterReturning(Object ret9){...}
public Object myInterceptor(ProceedingJoinPoint p) throws Throwable {...}
// 核心功能中抛出异常时执行
public void myThrows(Exception ex){
System.out.println("throws");
System.out.println("===="+ex.getMessage());
}
}
- 编织:
<aop:config>
<aop:aspect ref="myAdvicee">
<aop:pointcut id="pc" expression="execution(* com.zhj.service.UserServiceImpl.*(..))"/>
<aop:before method="beforezhj" pointcut-ref="pc"/>
<!-- returning="ret9" myAfterReturning方法的参数 -->
<aop:after-returning method="myAfterReturning" returning="ret9" pointcut-ref="pc"/>
<aop:around method="myInterceptor" pointcut-ref="pc"/>
<!-- throwing="ex" myThrows方法的参数 -->
<aop:after-throwing method="myThrows" pointcut-ref="pc" throwing="ex"/>
<aop:after ...><!-- after-returning:目标返回值后执行
after:目标之后必定执行,即使目标抛异常,比前者更强硬 -->
</aop:aspect>
</aop:config>
切入点表达式
- execution:
1>* com.service.UserServiceImpl.queryUser(..)
返回值:任意
包:com.service
类:UserServiceImpl
方法:queryUser
参数表:任意
2>* com.service.UserServiceImpl.*(..)
返回值:任意
包:com.service
类:UserServiceImpl
方法:所有,任意
参数表:任意
3>* com.service.*.*(..)
返回值:任意
包:com.service
类:所有,任意
方法:所有,任意
参数表:任意
4>* com..*.*(..)
返回值:任意
包:com包,及其所有子包(com.a, com.b, com.c.cc)
类:所有,任意
方法:所有,任意
参数表:任意
5>* *(..) 不建议
返回值:任意
包:任意
类:所有,任意
方法:所有,任意
参数表:任意
6>* com.service.UserServiceImpl.query*(..) 【技巧:批量切入】
返回值:任意
包:com.service
类:UserServiceImpl
方法:所有,任意
参数表:任意
*注意:尽量精确,避免不必要的切入
- within:
within(com.service.UserServiceImpl) 类中的所有方法
within(com..UserServiceImpl) com包和com子包下的UserServiceImpl类中的所有方法
尽量写精确,Spring查找效率高
<aop:pointcut id="pc" expression="within(com..UserServiceImpl)"/>
- args:
args(int,String,com.entity.User) 参数表如此的方法
<aop:pointcut id="pc" expression="args(int,String,com.entity.User)"/>
- 联用:
and or not
与 或 非
多个表达式联合使用Spring查找效率更高,如within + args的组合
一定是不同种类的表达式之间使用
not不能用在第一个表达式前,即不能放在最开始
<aop:pointcut id="pc" expression="execution(* com.zhj.service.UserServiceImpl.*(..)) and args(com.User)"/>
<aop:pointcut id="pc" expression="within(com.service.UserServiceImpl) or args(com.User)"/>
<aop:pointcut id="pc" expression="within(com.service.UserServiceImpl) and not args(com.User)"/>
@AspectJ注解
使用AspectJ注解简化开发
@Aspect //AspectJ的注解
public class MyAdvice2 {
private final String pc = "execution(* com.xxx.service.UserServiceImpl.*(..))";
@Before(pc) //AspectJ的注解
public void myBefore(){
System.out.println("before advice~~~~~~~03!!!!!");
}
@AfterReturning(value = pc,returning = "ret03") //AspectJ的注解
public void myAfter(Object ret03){
System.out.println("after~~~~~:"+ret03);
}
@Around(pc) //AspectJ的注解
public Object myInterceptor(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("tx begion~~~~");
Object ret = joinPoint.proceed();//target的方法执行
System.out.println("tx end~~");
return ret;//向上返回
}
@AfterThrowing(value = pc,throwing = "ex") //AspectJ的注解
public void myThrows(Exception ex){
System.out.println("ex~~~~:"+ex.getMessage());
}
}
<!-- 开启 @AspectJ支持 -->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
@Component注解
<!-- 可以扫描包下的所有类,类中的注解会被识别 -->
<context:component-scan base-package="com.zhj.advice"/>
@Aspect
@Component // <bean ...>
public class MyAdvice2 {
//...
}
切面的执行顺序
当多个切面,经过同一个方法时,默认先声明(配置文件中)的切面先经过
可使用 order 属性明确定义顺序(数值小的优先执行)
- 配置文件方式:
<!-- Advice -->
<bean id="aspect03" class="com.zhj.advice.MyAdvice"/>
<bean id="aspect04" class="com.zhj.advice.MyAdvice3"/>
<aop:config proxy-target-class="true">
<aop:aspect ref="aspect03" order="2">
<aop:pointcut id="pc03" expression="execution(* com.zhj.service.UserServiceImpl.queryUsers(..))"/>
<aop:before method="myBefore" pointcut-ref="pc03"/>
</aop:aspect>
<aop:aspect ref="aspect04" order="1">
<aop:pointcut id="pc03" expression="execution(* com.zhj.service.UserServiceImpl.queryUsers(..))"/>
<aop:before method="myBefore" pointcut-ref="pc03"/>
</aop:aspect>
</aop:config>
- 注解方式:
@Aspect
@Order(1) //效果同配置文件
public class MyAdvice2 {
//...
}
如果项目中两种定制方式都存在,order 依然有效
手动自动代理
如果某些类不在 IOC 容器中,想要切入,或者想脱离IOC容器单独AOP
/**
* Spring提供如下实现,可以自定义 Advice 组件
* MethodBeforeAdvice
* AfterReturningAdvice
* MethodInterceptor
* ThrowsAdvice
*/
public class MyAdviceDemo implements MethodBeforeAdvice {//前置额外功能
@Override
public void before(Method method, Object[] objects, Object o) throws Throwable {
System.out.println("before");
}
}
class Test{
public static void main(String[] args) {
//代理工厂
ProxyFactory factory = new ProxyFactory(new User());
// factory.addInterface(XXX.class);//没有接口可以不写,则会使用CGLIB代理
factory.addAdvice(new MyAdviceDemo());
User proxy = (User) factory.getProxy();
// this is a method call on the proxy!
proxy.getId();
}
}
源码解析
构造 --> set --> before init --> init --> post init --> destory
init很少用一般不写
后处理:before init + post init,对bean的再加工,力度较强
代理的定制过程发生在 post init 过程中
AbstractAutowireCapableBeanFactory___doCreateBean{
//.....
if (instanceWrapper == null) {
instanceWrapper = createBeanInstance(beanName, mbd, args);//完成单例bean创建
}
//.....
try {
populateBean(beanName, mbd, instanceWrapper);`
if (exposedObject != null) {
// 对bean后处理,beforeInit init postinit
exposedObject = initializeBean(beanName, exposedObject, mbd);
}
}
//.....
}
AbstractAutowireCapableBeanFactory___initializeBean(){//是否所有bean都经历这个过程??
//.....
if (mbd == null || !mbd.isSynthetic()) {
// BeforeInit
wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);`
}
try {
invokeInitMethods(beanName, wrappedBean, mbd);`//Init
}catch (Throwable ex) {
throw new BeanCreationException(...);`
}
if (mbd == null || !mbd.isSynthetic()) {
//AfterInit ,在此后处理中完成代理
wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);`
}
return wrappedBean;
}
AbstractAutowireCapableBeanFactory___applyBeanPostProcessorsAfterInitialization(){
Object result = existingBean;
// 遍历众多 BeanPostProcessor,其中有一个是:AbstractAutoProxyCreator,会完成代理的定制
for (BeanPostProcessor beanProcessor : getBeanPostProcessors()) {
result = beanProcessor.postProcessAfterInitialization(result, beanName);
if (result == null) {
return result;
}
}
return result;
}
AbstractAutoProxyCreator___postProcessAfterInitialization(){
if (bean != null) {
Object cacheKey = getCacheKey(bean.getClass(), beanName);`
if (!this.earlyProxyReferences.contains(cacheKey)) {
return wrapIfNecessary(bean, beanName, cacheKey);//完成从Target到Proxy的定制
}
}
return bean;
}
AbstractAutoProxyCreator___wrapIfNecessary(){
//.....
if (specificInterceptors != DO_NOT_PROXY) {
Object proxy = createProxy( bean.getClass(), beanName, specificInterceptors,
new SingletonTargetSource(bean));
//.....
}
//.....
}
AbstractAutoProxyCreator___createProxy(){
//.....
return proxyFactory.getProxy(getProxyClassLoader());//new ProxyFactory(),然后调用此方法,构建代理
//.....
}
ProxyFactory___getProxy(){
return createAopProxy().getProxy(classLoader);
}
ProxyFactory___createAopProxy(){
return getAopProxyFactory().createAopProxy(this); // 创建代理的Factory,Factory中getProxy获得代理
}
DefaultAOPProxyFactory___createAopProxy(){
// 是否积极优化,默认false,是否直接代理目标类(需要配置,默认false),是否有提供接口(如UserService)
if (config.isOptimize() || config.isProxyTargetClass() ||
hasNoUserSuppliedProxyInterfaces(config)) {
Class<?> targetClass = config.getTargetClass();
if (targetClass == null) {
throw new AopConfigException(...);
}
// 如果目标类是接口 如果目标类已是代理类
if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
return new JdkDynamicAopProxy(config);
}
return new ObjenesisCglibAopProxy(config);//使用cglib代理,其中的getProxy方法最终生成代理
}else {
return new JdkDynamicAopProxy(config);//使用jdk代理,其中的getProxy方法最终生成代理
}
}
如上:如果明确设置了使用cglib 或 目标类没有接口,且目标类不是接口,也不是已代理的类,则使用cglib代理,其他情况都使用jdk代理