反射(reflect)
反射:通過類的class對象來獲取類的信息,動態操作類中的字段、調用類中的方法。
獲取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"); //設置數組指定位置上的元素
反射的特點是動態操作,傳入該類的對象,根據傳入的對象動態進行操作。
代理(proxy)
代理是在原類的基礎上進行擴展、包裝,使之變成更加強大的類,代替原來的類來使用。aop就是使用代理實現的。
代理分爲2種
- 靜態代理
- 動態代理,常見的有jdk動態代理、cglib代理
靜態代理
//實現目標接口或繼承目標類
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();
靜態代理的特點
- 需要實現目標接口或者繼承目標類
- 可以代理接口、類
- 可以設置每個方法的增強,很靈活;但要手動設置每個方法的增強,又很繁瑣
不管實不實現目標接口、繼不繼承目標類,都可以做到對目標接口、目標類的增強,爲什麼要實現目標接口、繼承目標類,沒必要?
不管是參數還是返回值,都是用目標接口、目標類來聲明,不會用代理類來聲明,實現、繼承的意義在於成爲子類,可以作爲目標類使用,在目標類出現的地方都可以使用,代理要做的是取代,並非只是增強。
jdk動態代理
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動態代理
需要添加cglib的依賴
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.3.0</version>
</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動態代理的特點
- 需要實現MethodInterceptor接口
- 可以代理接口、類
- 通過代理調用的目標類的方法都會被增強,且所使用的增強完全一樣,偏死板
總結
如果要對不同的方法做不同的增強,用靜態代理;如果對每個方法的增強都一樣,用動態代理。
如果要代理接口,使用三種都行;如果要代理類,用靜態代理、cglib動態代理。