反射:通过类的class对象来获取类的信息,动态操作类中的字段、调用类中的方法。
// Class.forName("全限定类名")
Class<?> class1 = Class.forName("com.chy.mall.model.User"); //必须写成全类名。类名是String形式,编译时并不知道Class对象的类型,所以是?
Class<User> class2 = (Class<User>)class1; //强转
//类名.class
Class<User> class3=User.class;
//对象名.getClass()
User user=new User();
Class<? extends User> class4=user.getClass();
Class<User> userClass = User.class; //获取class对象
Constructor<User> constructor = userClass.getConstructor(int.class, String.class, int.class); //通过class对象获取指定的构造器,形参表要写成class对象的形式
User user = constructor.newInstance(1, "chy", 20); //传入实参创建实例
Class<User> userClass = User.class;
Field nameField = userClass.getDeclaredField("name"); //获取私有字段
nameField.setAccessible(true); //取消权限检查。private不允许在类外操作,需要取消权限检查才能操作private成员
User user = new User(1, "zhangsan", 20);
String name = (String) nameField.get(user); //传入对象,获取该字段的值,返回Object类型
nameField.set(user, "lisi"); //设置对象该字段的值
Class<User> userClass = User.class;
Method getNameMethod = userClass.getMethod("getName"); //获取无参的公有方法
Method getNameMethod = userClass.getMethod("setName", String.class); //获取带参的公有方法。后面是形参表的class对象,参数个数可变
User user = new User(1, "zhangsan", 20);
String name = (String) getNameMethod.invoke(user); //调用指定对象的该方法(无参),返回值是Object类型
setNameMethod.invoke(user, "lisi"); //调用指定对象的该方法(带参),后面是实参表,参数个数可变
//动态创建数组
Object obj = Array.newInstance(String.class, 10); //数组元素类型、元素个数,返回值是Object类型
String[] arr= (String[]) obj; //有需要的话可以强转
//动态操作数组。传入数组的元素类型是无法预测的,所以参数是Object类型
Object first = Array.get(obj, 0); //获取数组指定位置(index)上的元素,返回Object
Array.set(obj, 0, "zhangsan"); //设置数组指定位置上的元素
反射的特点是动态操作,传入该类的对象,根据传入的对象动态进行操作。
代理是在原类的基础上进行扩展、包装,使之变成更加强大的类,代替原来的类来使用。aop就是使用代理实现的。
代理分为2种
//实现目标接口或继承目标类
public class UserDaoProxy implements UserDao{
private UserDao userDao; //把要代理的接口或类写作成员变量。声明为接口,则可以代理这个接口所有的实现类;声明为实现类,则只代理这个实现类
//注入目标对象
public UserDaoProxy(UserDao userDao) {
this.userDao = userDao;
}
@Override
public void addUser() {
//..... //前增强
userDao.addUser(); //调用目标类的方法,如果是继承目标类,通过super来调用
//..... //后增强
}
@Override
public void deleteUser() {
userDao.deleteUser(); //如果不需要增强,直接调用目标类的方法即可
}
}
UserDao userDao = new UserDaoImpl();
UserDaoProxy userDaoProxy = new UserDaoProxy(userDao);
userDaoProxy.addUser();
静态代理的特点
不管实不实现目标接口、继不继承目标类,都可以做到对目标接口、目标类的增强,为什么要实现目标接口、继承目标类,没必要?
不管是参数还是返回值,都是用目标接口、目标类来声明,不会用代理类来声明,实现、继承的意义在于成为子类,可以作为目标类使用,在目标类出现的地方都可以使用,代理要做的是取代,并非只是增强。
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
//不需要继承、实现目标接口、目标类
public class UserDaoProxyFactory {
private UserDao userDao; //jdk动态代理代理的是接口,要声明为接口
//注入目标对象
public UserDaoProxyFactory(UserDao userDao) {
this.userDao = userDao;
}
//获取代理,代理是目标接口的实例
public UserDao getProxyInstance(){
ClassLoader classLoader = userDao.getClass().getClassLoader(); //获取目标类的类加载器。
Class<?>[] interfaces = userDao.getClass().getInterfaces(); //获取目标类实现的所有接口的class对象,这也是为什么要声明为接口的原因
InvocationHandler invocationHandler = new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { //后2个参数是目标方法、实参表
//..... //前增强
Object returnValue=method.invoke(userDao,args); //调用目标方法,传入目标对象、实参表
//.... //后增强
return returnValue; //返回目标方法的返回值,Object类型
}
};
//创建代理,返回值是Object类型,需要强转
Object userDaoProxy = Proxy.newProxyInstance(classLoader, interfaces, invocationHandler);
//强转为目标接口类型
return (UserDao)userDaoProxy;
}
}
UserDaoImpl userDao = new UserDaoImpl();
//传入目标对象,创建代理,代理是目标接口类型
UserDao userDaoProxy = new UserDaoProxyFactory(userDao).getProxyInstance();
userDaoProxy.addUser();
看invoke()的参数,在编译时不能确定要增强的方法,运行时根据传入的参数动态进行增强,所以叫做动态代理。静态代理在编译时就确定了要增强的方法。
是使用jdk自带的反射实现的动态代理,所以叫做jdk动态代理。
jdk动态代理的特点
需要添加cglib的依赖
<dependency>
<groupId>cglibgroupId>
<artifactId>cglibartifactId>
<version>3.3.0version>
dependency>
//需要实现MethodInterceptor接口
public class UserDaoProxyFactory implements MethodInterceptor {
private UserDao userDao; //可以是接口,也可以是类
//传入目标对象
public UserDaoProxyFactory(UserDao target) {
this.userDao = target;
}
//拦截目标方法,进行增强
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
//..... //前增强
Object returnValue = method.invoke(userDao, objects); //调用目标对象的方法
//..... //后增强
return returnValue;
}
//创建代理,代理是目标接口类型
public UserDao getProxyInstance(){
Enhancer en = new Enhancer(); //创建工具类对象
en.setSuperclass(userDao.getClass()); //设置基类(父类),即继承目标类
en.setCallback(this); //设置回调函数,此句代码是调用intercept()拦截目标方法,进行增强
return (UserDao) en.create(); //创建并返回代理对象。创建的对象是Object型,需要强转
}
}
UserDao userDao = new UserDaoImpl();
//传入目标对象,创建代理,代理是目标类型
UserDao userDaoProxy = new UserDaoProxyFactory(userDao).getProxyInstance();
userDaoProxy.addUser();
cglib动态代理的特点
如果要对不同的方法做不同的增强,用静态代理;如果对每个方法的增强都一样,用动态代理。
如果要代理接口,使用三种都行;如果要代理类,用静态代理、cglib动态代理。