JDK动态代理和CGLIB动态代理编码
JDK动态代理【接口】:
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
* JDK动态代理接口
*/
public class DynamicProxy implements InvocationHandler {
private Object target;
public DynamicProxy(Object target) {
this.target = target;
}
public <T> T getProxy() {
return (T) Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
before();
Object result = method.invoke(target, args);
after();
return result;
}
private void after() {
System.out.println("after");
}
private void before() {
System.out.println("before");
}
public static void main(String[] args) {
DynamicProxy dynamicProxy = new DynamicProxy(new HelloImpl());
((Hello)dynamicProxy.getProxy()).say();
}
}
interface Hello {
String say();
}
class HelloImpl implements Hello {
@Override
public String say() {
System.out.println("say");
return "say";
}
}
CGLIB动态代理【类或接口】:
依赖:
<!-- https://mvnrepository.com/artifact/cglib/cglib -->
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.3.0</version>
</dependency>
编码:
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
/**
* cglib动态代理类+单例模式
*/
public class CGlibProxy implements MethodInterceptor {
private static CGlibProxy instance = new CGlibProxy();
private CGlibProxy() {}
public static CGlibProxy getInstance() {
return instance;
}
public <T> T getProxy(Class<T> cls) {
return (T) Enhancer.create(cls, this); //Enhancer类,增强类
}
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
before();
Object result = methodProxy.invokeSuper(o, objects);
after();
return result;
}
private void after() {
System.out.println("after");
}
private void before() {
System.out.println("before");
}
public static void main(String[] args) {
CGlibProxy.getInstance().getProxy(HelloImpl.class).say();
}
}
Spring AOP:
增强类:
package smart;
import org.springframework.aop.MethodBeforeAdvice;
import org.springframework.stereotype.Component;
import java.lang.reflect.Method;
/**
* 前置增强类,放入增强代码
*/
@Component
public class GreetingBeforeAdvice implements MethodBeforeAdvice {
@Override
public void before(Method method, Object[] objects, Object o) throws Throwable {
System.out.println("Before");
}
}
package smart;
import org.springframework.aop.AfterReturningAdvice;
import org.springframework.stereotype.Component;
import java.lang.reflect.Method;
/**
* 后置增强类
*/
@Component
public class GreetingAfterAdvice implements AfterReturningAdvice {
@Override
public void afterReturning(Object o, Method method, Object[] objects, Object o1) throws Throwable {
System.out.println("After");
}
}
package smart;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
/**
* 环绕增强org.aopalliance.intercept.MethodInterceptor该接口不是Spring提供的,借用AOP联盟提供的
*/
@Component
public class GreetingAroundAdvice implements MethodInterceptor {
@Override
public Object invoke(MethodInvocation methodInvocation) throws Throwable {
before();
Object result = methodInvocation.proceed();
after();
return result;
}
private void after() {
System.out.println("After...");
}
private void before() {
System.out.println("Before...");
}
}
package smart;
import org.springframework.aop.ThrowsAdvice;
import java.lang.reflect.Method;
/**
* 抛出增强
*/
@Component
public class GreetingThrowAdvice implements ThrowsAdvice {
/**
* 当被代理的方法内部抛出异常,会被该增强拦截
* 按理ThrowsAdvice接口有afterThrowing方法,参数是方法,参数,目标对象,异常对象等信息,可以获取记录日志或插入数据库
* 但点开接口看没有任何方法
*/
public void afterThrowing(Method method, Object[] args, Object target, Exception e) {}
}
package smart;
import org.aopalliance.intercept.MethodInvocation;
import org.springframework.aop.support.DelegatingIntroductionInterceptor;
import org.springframework.stereotype.Component;
/**
* 用这个增强类让被代理类动态实现Apology接口,XML配置如下:
* <bean id="greetingProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
* <property name="interfaces" value="smart.Apology"/> //需要动态实现的接口
* <property name="target" value="greetingImpl"/> //目标类
* <property name="interceptorNames" value="greetingIntroductionAdvice"/> //引入增强
* <property name="proxyTargetClass" value="true"/> //代理目标类,默认false代理接口,此时spring使用jdk动态代理,设置为true,spring用cglib动态代理
* </bean>
*/
@Component
public class GreetingIntroductionAdvice extends DelegatingIntroductionInterceptor implements Apology {
public Object invoke(MethodInvocation mi) throws Throwable {
return super.invoke(mi);
}
@Override
public void saySorry(String name) {
System.out.println("Sorry! " + name);
}
}
编程式:
package smart;
import org.springframework.aop.framework.ProxyFactory;
import org.springframework.stereotype.Component;
/**
* Spring AOP:前置增强(Before Advice)、后置增强(After Advice)、环绕增强(Around Advice)、抛出增强(Throws Advice)、引入增强(Introduction Advice)
* AOP术语:对方法的增强叫Weaving(织入),对类的增强叫Introduction(引入)
* 当某个类实现类A接口,并没有实现B接口,想要调用B接口的方法,即实现对类的功能增强,使用引入增强(参考《Spring Reference》),不用改写被代理类,在程序运行时动态实现接口
*
* 分编程式和声明式
* 增强类同时实现MethodBeforeAdvice和AfterReturningAdvice接口,执行一次addAdvice即可
*
* 声明式配置代理:
* <bean id="代理工厂bean名称" class="org.springframework.aop.framework.ProxyFactoryBean">
* <property name="interfaces" value="代理的接口全限定名"/>
* <property name="target" ref="接口实现类的bean名称"/>
* <property name="interceptorNames">
* <list>
* <value>拦截器名称(即增强类的bean名称)</value> (若只有一个增强类,可简化为<property name="interceptorNames" value="拦截器名称(即增强类的bean名称)"/>)
* </list>
* </property>
* </bean>
*/
public class GreetingAdviceTest {
public static void main(String[] args) {
/**
* 编程式配置代理
*/
ProxyFactory proxyFactory = new ProxyFactory(); //创建代理工厂,使用ProxyFactoryBean也可以,一回事
proxyFactory.setTarget(new GreetingImpl()); //注入目标类对象
proxyFactory.addAdvice(new GreetingBeforeAdvice()); //添加前置增强
proxyFactory.addAdvice(new GreetingAfterAdvice()); //添加后置增强
// proxyFactory.addAdvice(new GreetingAroundAdvice()); //添加环绕增强,将前置增强与后置增强功能结合起来
Greeting greeting = (Greeting)proxyFactory.getProxy(); //从代理工厂中获取代理
greeting.sayHello("Jack"); //调用代理的方法
}
}
interface Greeting {
void sayHello(String name);
}
@Component
class GreetingImpl implements Greeting {
@Override
public void sayHello(String name) {
System.out.println("hello, " + name);
}
}
interface Apology {
void saySorry(String name);
}
//Apology接口默认实现类
class ApologyImpl implements Apology {
@Override
public void saySorry(String name) {
System.out.println("Sorry!" + name);
}
}
声明式:
XML配置:
<bean id="greetingProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="interfaces" value="smart.Greeting"/>
<property name="target" value="greetingImpl"/>
<property name="interceptorNames">
<list>
<value>greetingBeforeAdvice</value>
<value>greetingAfterAdvice</value>
</list>
</property>
</bean>
package smart;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class GreetingAdviceTestForXML {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("spring_aop.xml");
Greeting greeting = (Greeting) context.getBean("greetingProxy");
greeting.sayHello("JACK");
//若使用引入增强,动态实现了Apology接口,将bean强转为Apology类型调用其方法即可
}
}
切面:
动态代理拦截了类中所有方法,需要在代码中对所拦截的方法名加以判断,不太优雅的做法。
Spring AOP也拦截了类中所有方法,可通过切面Advisor(增强类Advice+拦截匹配条件(Pointcut,切点,基于表达式的拦截条件)),将切面配置到ProxyFactory中生成代理。
<!-- 配置切面:增强+切点 -->
<!-- Spring AOP提供的其它切面类:
DefaultPointcutAdvisor:默认切面,可扩展它来自定义切面
NameMatchMethodPointcutAdvisor:根据方法名称进行匹配的切面
StaticMethodMatcherPointcutAdvisor:用于匹配静态方法的切面
-->
<bean id="greetingAdvisor" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
<property name="advice" ref="greetingAroundAdvice"/> <!-- 增强 -->
<property name="pattern" value="smart.GreetingImpl.good.*"/> <!-- 切点,正则表达式(对满足切点匹配条件的方法进行拦截),匹配类中所有方法名以字符串good开头的方法,.*表示匹配所有字符 -->
</bean>
<!-- 配置代理,注入切面 -->
<bean id="greetingProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="target" value="greetingImpl"/> <!-- 目标类 -->
<property name="interceptorNames" value="greetingAdvisor"/> <!-- 这里不再配置增强,而改成切面,切面中封装了增强和切点 -->
<property name="proxyTargetClass" value="true"/> <!-- 代理目标类 -->
</bean>
上面的方式随着项目扩大,代理配置会越来越多,改用Spring AOP提供的自动代理(扫描bean名称):
<!-- 配置自动代理,不能定义代理接口即interfaces属性,因为不知道这些bean实现了多少接口,所以只能代理类 -->
<!-- optimize属性默认false,为true对代理生成策略进行优化:类有接口就用JDK动态代理代理接口,没有接口就用cglib动态代理代理类
而proxyTargetClass属性为true强制代理类,不考虑代理接口的方式
cglib可以代理任何类,但cglib创建代理速度慢,创建代理后运行速度快,jdk相反
运行时采用cglib创建代理拖慢系统性能,建议系统初始化时用cglib创建代理放入spring的applicationContext中备用
BeanNameAutoProxyCreator只能匹配目标类,不能匹配指定方法,要想匹配方法,需要使用切面与切点
Spring AOP基于切面的自动代理生成器:DefaultAdvisorAutoProxyCreator
-->
<bean class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">
<property name="beanNames" value="*Impl"/> <!-- 只为后缀是Impl的bean生成代理 -->
<property name="interceptorNames" value=""/> <!-- 增强 -->
<property name="optimize" value="true"/> <!-- 是否对代理生成策略进行优化 -->
</bean>
进一步,Spring AOP基于切面的自动代理生成器:DefaultAdvisorAutoProxyCreator:
<!-- 配置切面:增强+切点 -->
<bean id="greetingAdvisor" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
<property name="pattern" value="smart.GreetingImpl.good.*"/>
<property name="advice" ref="greetingAroundAdvice"/>
</bean>
<!-- 这里无需再配置代理,代理由DefaultAdvisorAutoProxyCreator自动生成,它扫描所有切面类并为其自动生成代理 -->
<bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator">
<property name="optimize" value="true"/>
</bean>
以上的方式依然存在大量切面配置,若要拦截指定注解的方法,必须扩展DefaultPointcutAdvisor类自定义切面类,在配置文件中进行切面配置,繁琐,这种切面+代理的配置方式保留,但引入Spring+Aspectj的优秀方案,常用:
Spring集成的AspectJ与直接使用AspectJ不同,不用定义AspectJ类(它扩展了Java语法的一种新语言,需要特点编译器),而只需要使用AspectJ切点表达式(比正则表达式更友好):
注解的方式:
配置:
<?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.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
<!-- spring 2.5+提供的bean扫描特性,扫描指定包 -->
<context:component-scan base-package="smart"/>
<!-- proxy-target-class属性默认false只能使用JDK动态代理代理接口,为true使用CGLib动态代理代理目标类 -->
<!-- 不在需要大量配置切面和代理了 -->
<aop:aspectj-autoproxy proxy-target-class="true"/>
</beans>
编程:
package smart;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.DeclareParents;
import org.springframework.stereotype.Component;
/**
* Spring+AspectJ基于注解,通过AspectJ execution表达式拦截方法实现环绕增强
* @Aspect注解表名该类是Aspect切面类(Advisor)无需实现接口,定义任意名称方法即可,Around注解+execution表达式(AspectJ切点表达式)
* 方法参数ProceedingJoinPoint对象在AOP中叫做JoinPoint连接点,可以通过它获取如方法名、参数等方法的任何信息
* 在xml配置:
*
*/
@Aspect
@Component
public class GreetingAspect {
/**
* 切点表达式,execution()表示拦截方法,括号中定义需要匹配的规则
* 第一个*表示方法的返回值是任意的
* 第二个*表示匹配该类中所有的方法,改为指定方法名称可以匹配指定方法
* (..)表示方法的参数是任意的
* @param pjp
* @return
* @throws Throwable
*/
/**
* 除了@Around外,其它增强注解:@Before前置增强,@After后置增强,@Around环绕增强,@AfterThrowing抛出增强
* DeclareParents引入增强,AfterReturning返回后增强,可立即为Finally增强,相当于finally语句,在方法结束后执行,比After执行时机晚
*
* 若目标方法有指定注解如自定义一个注解@Log,使用AspectJ @annotation表达式拦截方法:
* 切点表达式改为@Around("@annotation(Log)")即可,括号里是要拦截的注解全限定名
*
* @param pjp
* @return
* @throws Throwable
*/
@Around("execution(* smart.GreetingImpl.*(..))")
public Object around(ProceedingJoinPoint pjp) throws Throwable {
before();
Object result = pjp.proceed();
after();
return result;
}
private void after() {
System.out.println("after");
}
private void before() {
System.out.println("before");
}
/**
* 在Aspect类中定义需要引入增强的接口,也就是运行时需要动态实现的接口,在接口是标注@DeclareParents注解,属性:
* value目标类
* defaultImpl引入的接口的默认实现类,默认实现会在运行时自动增强到目标类中
*/
@DeclareParents(value = "smart.GreetingImpl", defaultImpl = ApologyImpl.class)
private Apology apology;
}
JDK1.4没有注解,使用基于配置的Spring+AspectJ,基于配置的方式定义切面类:
<aop:config>
<aop:aspect ref="aspect类的的bean名称">
<aop:around method="around" pointcut="execution(* smart.GreetingImpl.*(..))"/>
</aop:aspect>
</aop:config>
AOP思维导图:
各类增强类型对应的解决方案:
Spring AOP整体架构UML类图:
JDK动态代理和CGLIB动态代理编码的更多相关文章
- JDK动态代理和CGLib动态代理简单演示
JDK1.3之后,Java提供了动态代理的技术,允许开发者在运行期间创建接口的代理实例. 一.首先我们进行JDK动态代理的演示. 现在我们有一个简单的业务接口Saying,如下: package te ...
- 基于JDK动态代理和CGLIB动态代理的实现Spring注解管理事务(@Trasactional)到底有什么区别。
基于JDK动态代理和CGLIB动态代理的实现Spring注解管理事务(@Trasactional)到底有什么区别. 我还是喜欢基于Schema风格的Spring事务管理,但也有很多人在用基于@Tras ...
- Spring -- <tx:annotation-driven>注解基于JDK动态代理和CGLIB动态代理的实现Spring注解管理事务(@Trasactional)的区别。
借鉴:http://jinnianshilongnian.iteye.com/blog/1508018 基于JDK动态代理和CGLIB动态代理的实现Spring注解管理事务(@Trasactional ...
- Spring <tx:annotation-driven>注解 JDK动态代理和CGLIB动态代理 区别。
基于JDK动态代理和CGLIB动态代理的实现Spring注解管理事务(@Trasactional)到底有什么区别. 我还是喜欢基于Schema风格的Spring事务管理,但也有很多人在用基于@Tras ...
- java的静态代理、jdk动态代理和cglib动态代理
Java的代理就是客户端不再直接和委托类打交道,而是通过一个中间层来访问,这个中间层就是代理.使用代理有两个好处,一是可以隐藏委托类的实现:二是可以实现客户与委托类之间的解耦,在不修改委托类代码的情况 ...
- jdk动态代理和cglib动态代理底层实现原理详细解析(cglib动态代理篇)
代理模式是一种很常见的模式,本文主要分析cglib动态代理的过程 1. 举例 使用cglib代理需要引入两个包,maven的话包引入如下 <!-- https://mvnrepository.c ...
- 代理模式之静态代理,JDK动态代理和cglib动态代理
代理模式,顾名思义,就是通过代理去完成某些功能.比如,你需要购买火车票,不想跑那么远到火车站售票窗口买,可以去附近的火车票代售点买,或者到携程等第三方网站买.这个时候,我们就把火车站叫做目标对象或者委 ...
- jdk动态代理和cglib动态代理的区别
一.原理区别: java动态代理是利用反射机制生成一个实现代理接口的匿名类,在调用具体方法前调用InvokeHandler来处理. 而cglib动态代理是利用asm开源包,对代理对象类的class文件 ...
- jdk 动态代理和 cglib 动态代理
原理区别: java动态代理是利用反射机制生成一个实现代理接口的匿名类,在调用具体方法前调用InvokeHandler来处理. 而cglib动态代理是利用asm开源包,对代理对象类的class文件加载 ...
- 动态代理:jdk动态代理和cglib动态代理
/** * 动态代理类:先参考代理模式随笔,了解代理模式的概念,分为jdk动态代理和cglib,jdk动态代理是通过实现接口的方式创建代理类的,cglib是通过继承类的方式实现的代理类的 * jdk动 ...
随机推荐
- MySQL的一些指令操作
这个连接的也不错: https://www.cnblogs.com/wangyueping/p/11258028.html 如何给MySQL数据可添加一个用户 首先以root身份登录到MySQL服务器 ...
- asp.net webapi自定义输出结果类似Response.Write()
asp.net webapi自定义输出结果类似Response.Write() [HttpGet] public HttpResponseMessage HelloWorld() { string ...
- MongoDB的使用学习之(一)开篇
本人是菜鸟-1级,整理这个系列,之所以用整理,而不是写,是因为本人不是从头自己读源码,一个一个字母翻译过来的,而是记录整个学习过程,查看别人好的文章,收集好的资料,并有自己的一些项目代码,并从中得到点 ...
- 第四讲 自定义Realm来实现身份认证
1.jdbcReam已经实现了从数据库中获取用户的验证信息,但是jdbcRealm灵活性太差.如果要实现自己的一些特殊应用时,将不能支持.这时,可以通过自定义Realm来实现身份的认证功能. 2.Re ...
- C# 获取系统环境数据
using System; using System.Data; using System.Text.RegularExpressions; using System.Threading; names ...
- nodejs 模板引擎
自制替换模板 template.js var fs = require('fs') var http = require('http') var server = http.createServer( ...
- dict/json转xml
在json转xml时,首先传进来的一定是一个dict,如果不是需要转一下,然后开始迭代,遇到dict则递归,如果是list则循环递归,否则认为是文字,将其写入,逻辑不复杂,因为为了代码循环不是太频繁, ...
- python3-filter
Python内建的filter()函数用于过滤序列. 和map()类似,filter()也接收一个函数和一个序列.和map()不同的是,filter()把传入的函数依次作用于每个元素,然后根据返回值是 ...
- Sass:@at-root
@at-root 从字面上解释就是跳出根元素.当你选择器嵌套多层之后,想让某个选择器跳出,此时就可以使用 @at-root.来看一个简单的示例: .a { color: red; .b { color ...
- CF429E Points and Segments
链接 CF429E Points and Segments 给定\(n\)条线段,然后给这些线段红蓝染色,求最后直线上上任意一个点被蓝色及红色线段覆盖次数之差的绝对值不大于\(1\),构造方案,\(n ...