代理(proxy)分为2种:
静态代理动态代理 动态代理常用的有jdk动态代理、cglib代理。
静态代理
1、新建User接口
1 public interface User {
2 void addUser();
3 void deleteUser();
4 void updateUser();
5 }
2、新建实现类UserImpl
1 public class UserImpl
implements User {
2 @Override
3 public void addUser() {
4 //模拟添加用户
5 System.out.println("正在添加用户"
);
6 System.out.println("已添加用户"
);
7 }
8
9 @Override
10 public void deleteUser() {
11 //模拟删除用户
12 System.out.println("正在删除用户"
);
13 System.out.println("已删除用户"
);
14 }
15
16 @Override
17 public void updateUser() {
18 //模拟修改用户信息
19 System.out.println("正在修改用户信息"
);
20 System.out.println("已修改用户信息"
);
21 }
22 }
3、新建代理类UserProxy,也实现User接口,对目标对象(的方法)进行增强
1 public class UserProxy
implements User {
2 //定义目标对象(要代理的对象)
3 private User user;
4
5 //构造函数,初始化目标对象
6 public UserProxy(User user){
7 this.user=
user;
8 }
9
10 @Override
11 public void addUser() {
12 System.out.println("开启事务...");
//前处理,模拟开启事务
13 user.addUser();
//调用目标对象相应的方法
14 System.out.println("提交事务...");
//后处理,模拟提交事务
15 }
16
17 @Override
18 public void deleteUser() {
19 System.out.println("开启事务..."
);
20 user.deleteUser();
21 System.out.println("提交事务..."
);
22 }
23
24 @Override
25 public void updateUser() {
26 System.out.println("开启事务..."
);
27 user.updateUser();
28 System.out.println("提交事务..."
);
29 }
30 }
4、使用代理。新建测试类Test
1 public class Test {
2 public static void main(String[] args){
3 UserImpl user =
new UserImpl();
//目标对象
4 UserProxy userProxy =
new UserProxy(user);
//代理
5 userProxy.addUser(); //调用代理类的方法
6 }
7 }
静态代理的特点
代理类、目标类需要实现同一接口编译时就已确定目标对象的类型(编译时类型,比如上例中编译时就知道目标对象的类型是User)目标对象只能必须是特定类型,比如上例中目标对象只能是User类型,UserProxy这个代理类只能代理User的实例。如果要代理其他类型的对象,需要再写代理类,这就很麻烦了,一种代理只能代理一种类型,太鸡肋了,我们一般不用静态代理。
因为目标对象的类型是固定的,静止不动的(静态的),所以这种代理方式叫做静态代理。
何谓代理?
代理是使用一个更强大的类(在原类的基础上进行功能扩展)来代替原来的类进行工作。
比如我在使用UserImpl类时,还想使用事务、记录日志等做一些其他的操作,这些操作不属于用户类的范畴,不能封装到UserImpl类中。这时就可以使用代理来对原来的类(方法)进行增强。代理类保留了原有类的所有功能,在此基础上扩展了其他功能,更加强大。
被代理的类(UserImpl类)叫做目标类,实现了代理的类(UserProxy类)叫做代理类。
JDK动态代理
1、新建接口User
2、新建实现类UserImpl
3、新建类ProxyFactory,用于创建代理对象
1 class ProxyFactory{
2 //目标对象。动态代理是你传给它什么目标对象,它就代理什么对象,能代理所有类型的对象,所以声明为Object
3 private Object target;
4
5 //构造器,初始化目标对象
6 public ProxyFactory(Object target) {
7 this.target =
target;
8 }
9
10 //获取代理对象。目标对象是Object,所以代理对象也是Object
11 public Object getProxyInstance(){
12 ClassLoader classLoader = target.getClass().getClassLoader();
//获取目标类的类加载器。可通过目标对象获取,也可通过目标类获取,但目标对象声明为Object,所以只能通过目标对象来获取(不知道目标类)
13 Class<?>[] interfaces = target.getClass().getInterfaces();
//获取目标类所实现的所有接口的Class对象。因为目标类可能实现了多个接口,所以用的是复数。
14 InvocationHandler invocationHandler =
new InvocationHandler() {
//创建InvocationHandler接口的实例。这里使用匿名内部类来创建
15 @Override
16 public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
17 System.out.println("前增强...");
//此处写前增强的代码。
18 Object returnValue=method.invoke(target,args);
//调用目标方法
19 System.out.println("后增强...");
//此处写后增强的代码
20 return returnValue;
21 }
22 };
23
24
25 Object proxyInstance = Proxy.newProxyInstance(classLoader, interfaces, invocationHandler);
//创建代理对象,参数就是上面三个参数
26 return proxyInstance;
//返回代理对象
27 }
28 }
4、新建测试类Test
1 public class Test {
2 public static void main(String[] args){
3 UserImpl user =
new UserImpl();
//目标对象
4 Object obj =
new ProxyFactory(user).getProxyInstance();
//创建代理对象
5 User userProxy=(User)obj;
//返回值是Object,需要强转。只能强转为目标类所实现的接口类型,如果强转为目标类的类型则代理失败(报错)
6 userProxy.addUser();
//通过代理对象来调用方法
7 }
8 }
我们看下实现 InvocationHandler接口中的 这段代码:
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
System.out.println("前增强...");
//此处写前增强的代码
Object returnValue=method.invoke(target,args);
//调用目标方法
System.out.println("后增强...");
//此处写后增强的代码
return returnValue;
}
invocation是调用的意思,invoke也是调用的意思。InvocationHandler接口是用来指定如何调用目标类中的方法,按照写的实现来调用。
invoke这个函数就是反射中调用指定方法的函数。
java.lang.reflect中调用某个类中的方法: Object invoke(Object proxy, Method method, Object[] args)
第一个参数指定目标对象,第二个参数指定要调用的方法,第三个参数是所调方法需要的实参值,参数个数不确定,可写为数组形式。invoke()的返回值就是所调方法的返回值,只不过声明为了Object。
通过代理对象调用方法: userProxy.addUser();
会自动把目标对象、要调用的方法、实参表向下传递给invoke(),invoke执行完以后自动把返回值向上传回来。
jdk动态代理,是指用jdk自带的东西(反射)来实现动态代理,所以又叫java动态代理,不是说要代理jdk。
前后增强的代码可以放在函数中,然后在invoke()中调用对应的函数。
JDK动态代理的特点
目标类必须继承接口,创建代理的类无需继承接口可以代理任何类型的类。传什么类型的目标类,就代理什么类型,所有叫做动态代理。所设置的前后增强,是所有目标类中所有方法都会添加(执行)的。静态代理可以一个方法一个方法地设置,可以根据需要具体设置每一个方法的增强。jdk动态代理是一棍子打死,全都使用相同的前后增强。
CGLIB动态代理
静态代理、jdk动态代理都要求目标类必须实现一个或多个接口,如果目标类没有实现接口,则代理失败。
cglib代理不要求目标类实现接口。
1、如果是单独使用cglib,需要导入cglib.jar包以及cglib的依赖包asm.jar,不推荐,这样导包很容易出问题(cglib、asm的版本要对应)。
如果使用maven,则会自动导入依赖的asm.jar。
如果使用了spring,在spring的核心包spring-core.jar中已经内嵌了cglib,不必再导包。
2、新建类User,不必实现任何接口
3、新建创建代理对象的类ProxyFactory,需实现MethodInterceptor接口
1 class ProxyFactory
implements MethodInterceptor {
2 //目标对象。声明为Object
3 private Object target;
4
5 //构造器,初始化目标对象
6 public ProxyFactory(Object target) {
7 this.target =
target;
8 }
9
10 //给目标对象创建一个代理对象
11 public Object getProxyInstance(){
12 Enhancer en =
new Enhancer();
//创建工具类对象
13 en.setSuperclass(target.getClass());
//设置父类
14 en.setCallback(
this);
//设置回调函数。这句代码是给代理类对象设置拦截intercept()。前面只是继承了目标类,此处设置拦截(在拦截中实现增强、调用目标方法)
15 return en.create();
//创建代理对象并返回
16
17 }
18
19 //拦截
20 @Override
//cglib代理仍是在反射的基础上扩展而来的,所以参数和反射调用方法的参数差不多目标对象、背调方法、实参表、方法代理
21 public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy)
throws Throwable {
22 System.out.println("前增强...");
//前增强代码
23 Object returnValue = method.invoke(target, objects);
//传入目标对象、实参表。
24 System.out.println("后增强...");
//后增强代码
25 return returnValue;
//返回被调函数的返回值,是作为Object返回的
26 }
27
28 }
4、新建测试类Test
1 public class Test {
2 public static void main(String[] args){
3 User user =
new User();
//目标对象
4 Object obj =
new ProxyFactory(user).getProxyInstance();
//创建代理对象
5 User userProxy=(User) obj;
//返回值是Object,需要强转为目标类对象。
6 userProxy.addUser();
//通过代理对象来调用方法
7 }
8 }
CGLIB动态代理的特点
可以代理任何类,但目标类不能用final修饰,因为代理类要继承目标类;目标类中的方法不能使用final(要改写,前后增强)、static(要求是实例方法)修饰。目标类可以实现接口,也可以不实现任何接口。目标类中的所有方法都会被增强
如果目标类实现了接口,使用jdk、cglib都行;如果目标类没有实现任何接口,则使用cglib。
转载于:https://www.cnblogs.com/chy18883701161/p/11391061.html
相关资源:linux查看端口是否被占用的小脚本