Spring Aop&proxy到底是什么关系
proxy代理模式,是一种设计模式,具体内容可参见 https://www.cnblogs.com/cenyu/p/6289209.html
Spring Aop,是利用了代理模式在运行期间对植入了增强的代码
- 如果Spring bean没有用到aop(即没有aop会作用到当前bean)时,这个时候的bean就是一个普通的bean(利用反射机制创建的) 可以使用
org.springframework.aop.support.AopUtils.isAopProxy(proxy),来判断是不是代理增强的bean - 可以使用 org.springframework.aop.support.AopUtils.isJdkDynamicProxy(proxy)
判断是不是JDK动态代理生成的bean - 可以使用
org.springframework.aop.support.AopUtils.isCglibProxy(proxy)
判断是不是CGLIB动态代理生成的bean
Aop使用方式1(配置一个包路径作为切入点)
配置一个包路径作为切入点,也就是说被切面的类自己毫无感知
示例代码
package com.subject.spring.aop;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
@Aspect
@Component
public class LogAspect {
public LogAspect() {
System.out.println("构造器LogAspect加载..." +this);
}
@Pointcut("execution(public * com.subject.spring.*.*(..))")
public void pointcut() {
}
//这里的"pointcut()"需要与上面的方法名 pointcut() 保持一致
@Around("pointcut()")
public Object aroundMethod(JoinPoint joinPoint) throws Throwable {
try {
System.out.println("start。。。");
return ((ProceedingJoinPoint) joinPoint).proceed();
} catch (Throwable e) {
System.out.println("Throwable。。。");
throw e;
} finally {
System.out.println("end。。。");
}
}
}
Aop使用方式2(配置一个注解作为切入点)
配置一个注解作为切入点,被切面的类需要在相应的方法中引入
@Aspect
public class ApiMonitorProcess implements BeanPostProcessor {
private static final Logger LOGGER = LoggerFactory.getLogger(ApiMonitorProcess.class);
@Pointcut("@annotation(com.jd.o2o.commons.monitor.ApiMonitor)")
public void pointcut() {
}
@Around("pointcut()")
public Object aroundProcess(ProceedingJoinPoint pjp) throws Throwable {
Object apiEntity = pjp.getTarget();
Method method = ((MethodSignature)pjp.getSignature()).getMethod();
String interfaceName = apiEntity.getClass().getName();
String apiName = interfaceName + "." + method.getName();
Object apiRtn = null;
Throwable e = null;
long startTime = System.nanoTime();
long killTime = 0L;
String killTimeStr = null;
try {
apiRtn = pjp.proceed(pjp.getArgs());
} catch (Throwable var17) {
e = var17;
} finally {
killTime = System.nanoTime() - startTime;
killTimeStr = numberFormat.format(killTime);
}
this.log(interfaceName, apiName, method, pjp.getArgs(), apiRtn, killTimeStr, e);
if(e != null) {
throw e;
} else {
return apiRtn;
}
}
}
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface ApiMonitor {
int mLog() default 0;
String mKey() default "";
int mPeriod() default 1;
MonitorTag[] mTags() default {MonitorTag.Log};
}
使用方式
@ApiMonitor
public String say(String a){
System.out.println("say a=" + a);
return a+a;
}
spring aop的完整示例
spring-config.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.1.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.1.xsd">
<!-- 强制使用cglib代理-->
<aop:aspectj-autoproxy expose-proxy="true"/>
<context:component-scan base-package="com.subject.spring" use-default-filters="true">
</context:component-scan>
<!--<bean class="com.subject.spring.aop.LogAspect" />-->
</beans>
LogAspect类
package com.subject.spring.aop;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
@Aspect
@Component
public class LogAspect {
public LogAspect() {
System.out.println("构造器LogAspect加载..." +this);
}
@Pointcut("execution(public * com.subject.spring.*.*(..))")
public void pointcut() {
}
//这里的"pointcut()"需要与上面的方法名 pointcut() 保持一致
@Around("pointcut()")
public Object aroundMethod(JoinPoint joinPoint) throws Throwable {
try {
System.out.println("start。。。");
return ((ProceedingJoinPoint) joinPoint).proceed();
} catch (Throwable e) {
System.out.println("Throwable。。。");
throw e;
} finally {
System.out.println("end。。。");
}
}
}
具体被代理的类OneBean
package com.subject.spring;
import org.springframework.stereotype.Service;
@Service
public class OneBean {
public OneBean() {
System.out.println("构造器OneBean加载..." +this);
}
public String say(String a){
System.out.println("say a=" + a);
return a+a;
}
public String say2(String a){
System.out.println("say2 a=" + a);
return a+a;
}
}
测试工具类
package com.subject.spring;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.aop.framework.AopProxy;
import org.springframework.aop.framework.AopProxyUtils;
import org.springframework.aop.support.AopUtils;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class SpringDemo {
private static final Logger logger = LoggerFactory.getLogger(SpringDemo.class);
private static ClassPathXmlApplicationContext classPathXmlApplicationContext;
public static void main(String[] args){
logger.info("aaa");
String paths[] = {"spring-config.xml"};
classPathXmlApplicationContext= new ClassPathXmlApplicationContext(paths);
OneBean bean=(OneBean)classPathXmlApplicationContext.getBean("oneBean");
// AopProxyUtils.
boolean a=AopUtils.isAopProxy(bean);
System.out.println("a=" + a);
boolean b=AopUtils.isJdkDynamicProxy(bean);
System.out.println("b=" + b);
boolean c=AopUtils.isCglibProxy(bean);
System.out.println("c=" + c);
bean.say("abcd");
bean.say2("hhhh");
}
}
测试结果
构造器LogAspect加载...com.subject.spring.aop.LogAspect@1b59d510
构造器OneBean加载...com.subject.spring.OneBean@535c97e4
构造器OneBean加载...com.subject.spring.OneBean$$EnhancerByCGLIB$$935c4a54@2c8c7d6
是否是代理bean=true
是否是JDK代理bean=false
是否是Cglib代理bean=true
start。。。
say a=abcd
end。。。
start。。。
say2 a=hhhh
end。。。
变数1:如果将上面OneBean类的say2方法加上final修饰后的执行结果呢
即:
public final String say2(String a){
System.out.println("say2 a=" + a);
return a+a;
}
测试结果
构造器LogAspect加载...com.subject.spring.aop.LogAspect@3582c132
构造器OneBean加载...com.subject.spring.OneBean@5f0f0625
构造器OneBean加载...com.subject.spring.OneBean$$EnhancerByCGLIB$$fe9e14e2@6482d603
是否是代理bean=true
是否是JDK代理bean=false
是否是Cglib代理bean=true
start。。。
say a=abcd
end。。。
say2 a=hhhh
变数1结论:被关键字final修饰的方法进行代理时直接忽略代理逻辑,直接执行原始类的方法
变数2:如果将上面OneBean类加上final修饰后的执行结果呢
即:
@Service
public final class OneBean {
public OneBean() {
System.out.println("构造器OneBean加载..." +this);
}
测试结果
构造器LogAspect加载...com.subject.spring.aop.LogAspect@26b31b77
构造器OneBean加载...com.subject.spring.OneBean@63f8247d
Exception in thread "main" org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'oneBean' defined in file [E:\github\javastudy\study-service\target\classes\com\subject\spring\OneBean.class]: Initialization of bean failed; nested exception is org.springframework.aop.framework.AopConfigException: Could not generate CGLIB subclass of class [class com.subject.spring.OneBean]: Common causes of this problem include using a final class or a non-visible class; nested exception is java.lang.IllegalArgumentException: Cannot subclass final class class com.subject.spring.OneBean
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:529)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:458)
at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:295)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:223)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:292)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:194)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:628)
at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:932)
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:479)
at org.springframework.context.support.ClassPathXmlApplicationContext.<init>(ClassPathXmlApplicationContext.java:139)
at org.springframework.context.support.ClassPathXmlApplicationContext.<init>(ClassPathXmlApplicationContext.java:93)
at com.subject.spring.SpringDemo.main(SpringDemo.java:16)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at com.intellij.rt.execution.application.AppMain.main(AppMain.java:144)
变数2结论:被关键字final修饰的类进行代理时直接抛出异常,程序停止运行
变数3:前面的测试结果(是否是Cglib代理bean=true)表明都是基于cglib的,那么如果不在spring-config.xml配置文件中强制指定要求cglib,即
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.1.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.1.xsd">
<!-- 强制使用cglib代理-->
<!--<aop:aspectj-autoproxy expose-proxy="true"/>-->
<context:component-scan base-package="com.subject.spring" use-default-filters="true">
</context:component-scan>
</beans>
测试结果
构造器LogAspect加载...com.subject.spring.aop.LogAspect@1b192059
构造器OneBean加载...com.subject.spring.OneBean@31a3ca10
是否是代理bean=false
是否是JDK代理bean=false
是否是Cglib代理bean=false
say a=abcd
say2 a=hhhh
变数3结论:如果不强制指定cglib,而且当被代理的bean(OneBean)没有实现接口时,这个bean最终是没有经过动态代理的bean,也就是没有被增加代码的bean