静态代理和jdk,cglib动态代理到Spring Aop的动态代理
什么是代理模式(Peoxy)
定义:
由于某些原因需要给某对象提供一个代理以控制对该对象的访问。因为访问对象不适合或者不能直接应用目标对象,代理对象就可以作为访问对象和目标对象之间的中介。
角色:
- 主体(Subject) 定义了使Proxy和Real Subject角色之间具有一致性的接口。
- 真实主体(Real Subject) 实现Subject角色接口,是代理对象所代表的真实对象。
- 代理(Proxy) 提供与Real Subject 相同方法,内部引用Real Subject角色。
优点:
- 代理模式在客户端与目标对象之间起到一个中介作用和保护目标对象的作用;
- 代理对象可以扩展目标对象的功能;
- 代理模式能将客户端与目标对象分离,在一定程度商降低了系统的耦合度
普通代理:
通过代理对象来访问角色,不能直接访问真实角色;真实角色的初始化放在代理中。
强制代理:
真实角色初始化后,不能直接使用,必须获取代理类,用代理类调用对应的方法。强制代理通常通过增加getProxy()方法实现获取方法,在真实类增加私有方法判断是否通过代理类调用。
静态代理和动态代理
静态代理
静态代理:代理类是自己手动实现的,自己创建的一个Java类代表代理类
案例
案例分析
1.创建一个Game接口,里面有一款名叫LOL的游戏
//定义游戏接口
public interface Game {
/**
* 接口定义一个方法,标识有LOL这个游戏
*/
void LOL();
}
2.创建一个游戏启动器,实现Game接口,要玩游戏
//定义一个启动类,即目标类,实现接口。
public class Player implements Game{
/**
* 私有化无参构造,不能直接获取目标对象,起到保护作用
*/
private Player() {
}
@Override
public void LOL() {
System.out.println("玩lol");
}
/**
* 构造getProxy()方法实现获取目标类
* @return 返回游戏厂家代理类
*/
public static GameFactory getProxy(){
Player player = new Player();
return new GameFactory(player);
}
}
3.创建一个游戏厂商代理类,实现Game接口。游戏厂家代理这个游戏,玩家需要通过代理厂家来玩游戏
//定义一个游戏代理类,即代理类
public class GameFactory implements Game{
private Player player;
/**
* 带参构造,传入目标对象
* @param player
*/
public GameFactory(Player player) {
this.player = player;
}
@Override
public void LOL() {
//代理商代理的游戏开始
System.out.println("游戏开始前,选英雄");
player.LOL();
//游戏结束
System.out.println("游戏结束了,胜利");
}
}
4.创建游戏客户端,玩家玩游戏
public class PlayGame {
public static void main(String[] args) {
//通过getProxy方法获得方法
GameFactory gameFactory = Player.getProxy();
gameFactory.LOL();
}
}
5.结果:
游戏开始前,选英雄
玩lol
游戏结束了,胜利
静态代理优缺点:
优点:
- 实现简单
- 容易理解
缺点:当项目中,目标类的代理很多,或者接口功能需要增强,修改,有以下缺点
- 当目标类增加了,代理类可能也需要成倍增加
- 当接口中功能增加,修改,会影响众多实现类,厂家类,代理类都需要进行修改
动态代理
使用jdk的反射机制,在程序执行中,创建代理对象。不需要创建类文件,代理目标是活动的,可设置的。
- 动态代理是指代理类对象在程序运行时由JVM根据反射机制动态生成的。动态代理不需要定义代理类的,java源文件。
- 动态代理其实就是jdk运行期间,动态创建class字节码并加载到JVM。
- 动态代理的实现方式常用的有两种:使用JDK代理,与通过CGLlB动态代理。
JDK动态代理
JDK动态代理通过反射来接收被代理的类,并且要求被代理的类必须实现一个接口,核心是InvocationHandler接口和Proxy类。
JDK动态代理具体实现原理:
- 通过实现InvocationHandler接口创建自己的调用处理器,重写invke()方法实现功能增强
- 通过为Proxy类指定ClassLoader对象和一组interface来创建动态代理
- 通过反射机制获取动态代理类的构造函数,其唯一参数类型就是调用处理器接口类型
- 通过构造函数创建动态代理类实例,构造时调用处理器对象作为参数参入
案例
案例分析
还是以同样的案例
//定义游戏接口
public interface Game {
/**
* 接口定义一个方法,标识有LOL这个游戏
*/
void LOL();
}
//定义一个游戏启动类,即目标类,实现接口。
public class Player implements Game {
@Override
public void LOL() {
System.out.println("目标类Play中,执行玩的方法");
System.out.println("玩lol");
}
}
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
//实现InvocationHandler,完成代理类要完成的功能(1.调用目标方法,2.功能增强)
public class GameProxy implements InvocationHandler {
//这里用Object类,因为传入的target可以是任何类的
private Object target = null;
//动态代理,目标对象是活动的,不是固定的,需要传进来
public GameProxy(Object target) {
//赋值
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
/**
* Object proxy: jdk创建的代理对象,无需赋值
* Method method: 目标类中的方法
* Object[] args: 目标类中的方法参数
*/
System.out.println("游戏开始前,请选择英雄========="+method.getName());
Object o = method.invoke(target, args);
System.out.println("游戏失败,多练练狗头吧========"+method.getName());
return o;
}
}
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
//游戏测试类
public class PlayGameTest {
public static void main(String[] args) {
//创建代理对象 ,使用Proxy
//1.创建目标对象
Game game = new Player();
//2.创建InvocationHandler对象
InvocationHandler invocationHandler = new GameProxy(game);
//3.创建代理对象
Game proxy = (Game) Proxy.newProxyInstance(game.getClass().getClassLoader(),
game.getClass().getInterfaces(),
invocationHandler);
//通过代理执行方法
proxy.LOL();
}
}
结果:
游戏开始前,请选择英雄=LOL
目标类Play中,执行玩的方法
玩lol
游戏失败,多练练狗头吧LOL
CGLIB动态代理
- CGBLIB 利用ASM开源包,对代理对象类的class文件加载进来,通过修改其字节码生成子类来处理。
- CGLib动态代理是通过字节码底层继承要代理类来实现,因此如果被代理类被final关键字所修饰,会失败。
- 使用 JDK 的 Proxy 实现代理,要求目标类与代理类实现相同的接口。若目标类不存在接口,则无法使用该方式实现。但对于无接口的类,要为其创建动态代理,就要使用 CGLIB 来实现。
案例
案例分析
相同的案例,用CGLIB代理,就不用创建接口类。
public class Player02 {
public void play(String name){
System.out.println("我是玩家,我要玩"+name);
}
}
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
public class CglibProxy implements MethodInterceptor {
private Object target;
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
/**
* Object o : 代理对象本身
* Method method : 被代理对象的方法
* Object[] objects : 函数调用的参数
* MethodProxy methodProxy : 方法的代理
*/
System.out.println("CGLIB动态代理,游戏开始!");
Object invoke = method.invoke(target, objects);
System.out.println("CGLIB动态代理,游戏结束");
return invoke;
}
public Object getCglibProxy(Object objectTarget){
this.target = objectTarget;
Enhancer enhancer = new Enhancer();
//设置父类,因为CGLIB是针对指定类生成一个子类,所以需要指定父类
enhancer.setSuperclass(objectTarget.getClass());
enhancer.setCallback(this);//设置回调
return enhancer.create();//创建并返回代理对象
}
}
public class CglibTest {
public static void main(String[] args) {
//实例化对象
CglibProxy cglibProxy = new CglibProxy();
//获取代理对象
Player02 player = (Player02) cglibProxy.getCglibProxy(new Player02());
player.play("CF");
}
}
注意:在使用CGLIB动态代理,需要导入CGLIB的jar包
Spring Aop中两种动态代理的实现方式
Spring中JDK动态代理
案例分析
1.创捷UserService接口
public interface UserService {
//新增用户
void addUser(String userName,String passWord);
//删除用户
String delUser(String userName);
}
2.创建UserService接口实现类UserServiceImpl
import com.XiaoWu.JDK.UserService;
import org.springframework.stereotype.Service;
@Service
public class UserServiceImpl implements UserService {
@Override
public void addUser(String userName, String passWord) {
System.out.println("调用新增用户方法");
System.out.println("传入参数为userName:"+userName+",passWord:"+passWord);
}
@Override
public String delUser(String userName) {
System.out.println("调用了删除方法");
return "删除成功"+userName;
}
}
3.代理对象,对目标的方法进行增强
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;
//@Aspect:作用是把当前类标识为一个切面供容器读取
@Aspect
@Component
public class UserServiceAspect {
//前置增强
//execution(访问权限 包名.类名。方法名(..));args:传入的参数
@Before("execution(* com.XiaoWu.JDK.UserService.addUser(..)) && args(userName,passWord)")
public void before(String userName,String passWord){
System.out.println("JDK加强后的方法-----before,传入值userName:"+userName+",passWord:"+passWord);
}
//后置增强,带返回值
@AfterReturning(value = "execution(* com.XiaoWu.JDK.UserService.delUser(..)) && args(userName)",argNames = "userName,rs",returning = "rs")
public String afterReturning(String userName,String rs){
System.out.println("JDK加强后的方法----AfterReturning,传入值userName:"+userName);
return rs;
}
}
4.注解类
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
@Configuration
@ComponentScan(basePackages = "com.XiaoWu.JDK")
@EnableAspectJAutoProxy
public class AopConfig {
}
5.测试类
import com.XiaoWu.JDK.config.AopConfig;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class test {
public static void main(String[] args) {
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(AopConfig.class);
UserService bean = applicationContext.getBean(UserService.class);
bean.addUser("张三","123");
System.out.println("============");
String lisi = bean.delUser("李四");
System.out.println(lisi);
System.out.println(bean.getClass().getSuperclass());
}
}
6.结果
JDK加强后的方法-----before,传入值userName:张三,passWord:123
调用新增用户方法
传入参数为userName:张三,passWord:123============
调用了删除方法
JDK加强后的方法----AfterReturning,传入值userName:李四
删除成功李四class java.lang.reflect.Proxy
Spring中CGLIB动态代理
案例分析
1.创建Student对象
import org.springframework.stereotype.Component;
@Component
public class Student {
public void hello(){
System.out.println("hello,student!!!");
}
}
2.创建Student代理类,对目标方法进行增强
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;
@Component
@Aspect
public class StudentAspect {
@Around("execution(* com.XiaoWu.CGLib.Student.hello(..))")
public void around(ProceedingJoinPoint joinPoint){
System.out.println("CGLib环绕增强前");
try {
joinPoint.proceed();
} catch (Throwable e) {
e.printStackTrace();
}
System.out.println("CGLib环绕增强后");
}
}
3.注解类
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import org.springframework.stereotype.Component;
@Component
@ComponentScan(basePackages = "com.XiaoWu.CGLib")
@EnableAspectJAutoProxy
public class Config {
}
4测试类
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class Test {
public static void main(String[] args) {
ApplicationContext context = new AnnotationConfigApplicationContext(Config.class);
Student bean = context.getBean(Student.class);
bean.hello();
System.out.println(bean.getClass().getSuperclass());
}
}
5.结果
CGLib环绕增强前
hello,student!!!
CGLib环绕增强后
class com.XiaoWu.CGLib.Student
JDK和CGLIB的原理区别,以及优缺点
- java动态代理是利用反射机制生成一个实现代理接口的匿名类,在调用具体方法前调用InvokeHandler来处理。
- 而cglib动态代理是利用asm开源包,对代理对象类的class文件加载进来,通过修改其字节码生成子类来处理。
1、如果目标对象实现了接口,默认情况下会采用JDK的动态代理实现AOP
2、如果目标对象实现了接口,可以强制使用CGLIB实现AOP
3、如果目标对象没有实现了接口,必须采用CGLIB库,spring会自动在JDK动态代理和CGLIB之间转换
4、使用JDK动态代理的父类是class java.lang.reflect.Proxy,使用CGLIB动态代理的父类是代理类所继承的目标类,如上就是class com.XiaoWu.CGLib.Student
5.使用JDK动态代理是自带的,不需要导入jar包,使用CGLIB动态代理需要导入相应的jar包
6.从执行效率上看,CGLIB动态代理效率较高。
本人还只是一名大三的学生,这是我在学习过程中通过查资料学习总结出来的。欢迎各位大佬们的指点!若涉及侵权,请联系我