1.一个接口两个实现
@Autowired注解在注入的过程中有两种方式:
(1)ByType和ByName,ByType在一个接口两个实现的情况下是绝对找不到的,所以只能采用ByName的方式
2.静态代理
2.1使用静态代理的原因
2.2静态代理的原理及使用
1.接口 2.代理 3.目标对象
接口代码:
package com.jt.mapper;
import com.jt.pojo.User;
public interface UserDao {
void addUser(User user);
}
目标代码:
package com.jt.service;
import com.jt.mapper.UserDao;
import com.jt.pojo.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service("target")
public class UserServiceImpl implements UserService {
@Autowired
private UserDao userDao;
@Override
public void addUser(User user) {
userDao.addUser(user);
}
}
代理代码块:
package com.jt.proxy;
import com.jt.mapper.UserDao;
import com.jt.pojo.User;
import com.jt.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service("userService")
public class StaticProxy implements UserService {
@Autowired
private UserService target;
@Override
public void addUser(User user) {
try {
System.out.println("事物开始");
target.addUser(user);
System.out.println("事务结束");
}catch (Exception e){
e.printStackTrace();
System.out.println("事务回滚");
}
}
}
测试代码:一定要按名字调用否则会报错
2.3静态代理的弊端
1).静态代理只针对于某个接口 不能实现所有接口的代理 实用性较差
2).静态代理中所有的方法,都需要手动的添加事务开始/事务提交代码 代码冗余 不够简洁.
3动态代理
原理:利用一个工厂动态的目标对象创建代理
需要注意的知识:匿名内部类 new invocationhandler
1.ClassLoader loader, 类加载器(获取目标对象的Class)
2.类<?>[] interfaces, JDK代理要求 必须有接口,java中可以多实现
3.InvocationHandler h 对目标方法进行扩展
代码中:
1.target目标对象
2.proxy代理对象 带目标对象的方法进行扩展
此代码块创建了一个代理对象:
Object proxy = Proxy.newProxyInstance(classLoader, interfaces, new InvocationHandler() {
package com.jt.proxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
//能否利用一个工厂动态为目标对象创建代理
public class JDKProxyFactory {
//要求用户传递目标对象
//关于匿名内部类用法说明: 匿名内部类引用外部参数 要求参数必须final修饰
public static Object getProxy(final Object target){
//1.调用java API实现动态代理
/**
* 参数分析: 3个参数
* 1.ClassLoader loader, 类加载器(获取目标对象的Class)
* 2.类<?>[] interfaces, JDK代理要求 必须有接口
* java中可以多实现
* 3.InvocationHandler h 对目标方法进行扩展
*/
//1.获取类加载器
ClassLoader classLoader = target.getClass().getClassLoader();
//2.获取接口数组
Class[] interfaces = target.getClass().getInterfaces();
//3.通过动态代理创建对象
Object proxy = Proxy.newProxyInstance(classLoader, interfaces, new InvocationHandler() {
//invoke方法: 代理对象调用方法时invoke执行,扩展方法的编辑位置
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//proxy: 代理对象本身
//method: 用户调用的方法对象
//args: 用户调用方法的参数
// result 标识目标方法执行的返回值
Object result = null;
try {
//添加事务的控制
System.out.println("事务开始");
//执行目标方法
// target真实的目标对象,method方法对象,args方法参数
result = method.invoke(target,args);
System.out.println("事务提交");
}catch (Exception e){
e.printStackTrace();
System.out.println("事务回滚");
}
return result;
}
});
return proxy;
}
}
动态代理优点
1可以使真实角色的操作更加纯粹,不用去关注一些公共的业务
2公共业务就交给代理角色,实现了业务分工
3公共业务发生扩展的时候,方便集中管理
4一个动态代理类代理的是一个接口,一般就是对应的一类业务(多个类实现同一个接口,只要重写方法,客户端可以通过修改代理的真实对象来实现动态的代理)
5多个房东同时卖房,他们的房子信息不一样,如果采用静态代理,需要为他们各自写一个代理类,就十分麻烦。我们可以采用动态代理的方法,让这些房东同时实现一个接口(Rent),这样我们在客户类只需要修改代理的真实对象,就可以得到每个房东各自的房子信息。