文章目录
一 反射
1.1 类加载
在class文件加载到jvm中时,会对应创建一个Class对象;分为三个步骤:加载、连接、初始化
加载
* 将class文件加载到内存区域,对应生成一个Class对象
连接
* 验证:验证类的结构是否正确
* 准备:初始化静态成员
* 解析:将字节转换成jvm能够执行的引用(对象、变量、方法)
初始化
* 将对象中的成员变量初始化
---------------
- 加载时机
* 创建类对象的实例
* 访问类的静态变量,或者为静态变量赋值
* 调用类的静态方法
* 使用反射方式来强制创建某个类或接口对应的java.lang.Class对象
Class.forName("com.mysql.jdbc.Driver");创建了Driver类的运行时对象
* 初始化某个类的子类
* 直接使用java.exe命令来运行某个主类
------------------------------
1.2 类加载器
负责将.class文件加载到内存中,并为之生成对应的Class对象
- 分类
Bootstrap ClassLoader 根类加载器
也被称为引导类加载器,负责Java核心类的加载
比如System,String等。在JDK中JRE的lib目录下rt.jar文件中
Extension ClassLoader 扩展类加载器
负责JRE的扩展目录中jar包的加载。
在JDK中JRE的lib目录下ext目录
Sysetm ClassLoader 系统类加载器
负责在JVM启动时加载来自java命令的class文件,以及classpath环境变量所指定的jar包和类路径
------------------------------
1.3 类反射机制
通过类的Class对象,动态去调用类中的属性和方法
- 获取Class对象方式
* 全类名=包名.类名
Class.forName("全类名")
* 编译期
类名.class
* 运行时
对象名.getClass()
------------------------------
1.4 反射结合工厂模式
工厂模式是我们最常用的实例化对象模式了,是用工厂方法代替new操作的一种模式
- 榨汁的案例,使用工厂设计模式
public class FruitFactory {
public static Fruit getFruit(String fruitName) throws Exception {
return (Fruit) Class.forName(fruitName).newInstance();
}
}
public class Demo05 {
public static void main(String[] args) throws Exception {
///获取苹果
Fruit fruit1 = FruitFactory.getFruit("com.qfedu.bean.Apple");
//获取香蕉
Fruit fruit2 = FruitFactory.getFruit("com.qfedu.bean.Banana");
//存在的问题:
//com.qfedu.bean.Apple对应的对象
//将以上"com.qfedu.bean.Apple"字符串写到java代码中,合适吗?
//不合适!!!全类名发生改变了,那你就必须修改java源代码,必须要重新部署项目!
//"com.qfedu.bean.Apple"字符串和java程序的耦合性非常高!!
//"com.qfedu.bean.Apple"字符串不能放到java代码中,而应该放置到配置文件中!!
//仅仅修改配置文件,是不需要重新部署工程的!
//这也就是为什么要将"com.mysql.jdbc.Driver"配置到jdbc.properties中!
//解耦!!降低耦合!!
//总结:
//配置文件+工厂模式+反射+注解+xml解析 就是spring框架的核心原理!!!
}
}
------------------------------
1.5 反射获取构造方法
- Constructor
Class类的newInstance()方法是使用该类无参的构造函数创建对象, 如果一个类没有无参的
构造函数, 就不能这样创建了,可以调用Class类的getConstructor(String.class,int.class)方
法获取一个指定的构造函数然后再调用Constructor类的newInstance("张三",20)方法创建对象
- 访问public修饰的构造器
//获取User类对应的Class对象
Class<?> clazz = Class.forName("com.qfedu.bean.User");
//获取无参构造方法对象
Constructor<?> c1 = clazz.getConstructor();
//使用无参创建User类对象
Object obj1 = c1.newInstance();
System.out.println(obj1);
//获取User类对应的有参构造方法对象
Constructor<?> c2 = clazz.getConstructor(Integer.class, String.class, String.class);
//使用有参创建User对象
Object obj2 = c2.newInstance(1, "张三", "root");
System.out.println(obj2);
---------------
- 访问非public修饰的构造器(使用暴力反射)
onstructor<?> c3 = clazz.getDeclaredConstructor(String.class, String.class);
//暴力反射,让私有构造器对象可以被外部访问
c3.setAccessible(true);
Object obj3 = c3.newInstance("tom", "20000101");
System.out.println(obj3);
------------------------------
1.6 反射获取成员变量
- Field
Class.getField(String)方法可以获取类中的指定字段(可见的), 如果是私有的可以用
getDeclaedField("name")方法获取,通过set(obj, "李四")方法可以设置指定对象上该字段的
值, 如果是私有的需要先调用setAccessible(true)设置访问权限,用获取的指定的字段调用
get(obj)可以获取指定对象中该字段的值
---------------
//获取User类的Class对象
Class<User> clazz = User.class;
User user = clazz.newInstance();
//操作public修饰的成员变量
Field idField = clazz.getField("id");
//设置该成员变量值
//obj:需要设置的对象
//value:需要设置的值
//给user对象的id属性设置值为250
idField.set(user,250);
System.out.println(user);
//获取该成员变量值
Object idValue = idField.get(user);
System.out.println(idValue);
//操作非public修饰的成员变量
Field usernameField = clazz.getDeclaredField("username");
usernameField.setAccessible(true);
usernameField.set(user,"坤坤");
System.out.println(user);
Object usernameValue = usernameField.get(user);
System.out.println(usernameValue);
------------------------------
1.7 反射获取方法
- Method
Class.getMethod(String, Class...) 和 Class.getDeclaredMethod(String, Class...)方法可以
获取类中的指定方法,调用invoke(Object, Object...)可以调用该方法
---------------
//获取User类的Class对象
Class<User> clazz = User.class;
User user = clazz.newInstance();
//获取public修饰的成员方法
Method method01 = clazz.getMethod("setPassword", String.class);
//使用方法对象
//obj:哪个对象在执行该方法
//args:方法执行时所需的参数值
method01.invoke(user,"123456");
System.out.println(user);
//操作非public修饰的成员方法
Method method02 = clazz.getDeclaredMethod("show");
method02.setAccessible(true);
Object result = method02.invoke(user);
System.out.println(result);
invoke方法后的返回值就是原有方法执行之后的返回值
------------------------------
1.8 反射越过泛型检查
java中的泛型的作用范围在编译期,也就是说在运行时,是没有泛型的!
反射技术,可以在程序运行时,动态地调用List类中add方法,往集合中添加任意类型的元素
//创建List集合对象
List<Integer> list = new ArrayList<Integer>();
list.add(1);
list.add(2);
list.add(3);
System.out.println(list);
//泛型只在编译期有效!!!
//反射越过泛型检查
//反射可以在程序运行时,动态地调用List中的add方法去添加元素
Class<? extends List> clazz = list.getClass();
Method add = clazz.getDeclaredMethod("add", Object.class);
add.setAccessible(true);
Object result = add.invoke(list, "hello , generic type !");
System.out.println(list);
------------------------------
1.9 反射通用方法
- 给指定对象的指定字段设置指定值
public static void main(String[] args) throws Exception {
User user = new User();
setValue(user,"id" , 123);
System.out.println(user);
}
/**
* 给指定对象的指定属性设置指定值
* @param obj : 指定对象
* @param fieldName : 指定属性
* @param value : 指定值
*/
public static void setValue(Object obj , String fieldName , Object value) throws Exception {
Class<?> clazz = obj.getClass();
//方法名规范:如果只有一个单词,所有的字母全都小写。如果有多个单词,从第二个单词开始,首字母大写!!!
//变量名规范: 如果只有一个单词,所有的字母全都小写。如果有多个单词,从第二个单词开始,首字母大写!!!
//username setUsername
//根据属性名称获取对应的set方法名称
// String methodName = "set" + "U" + "sername";
String methodName = "set" + fieldName.substring(0,1).toUpperCase() + fieldName.substring(1);
Field field = clazz.getDeclaredField(fieldName);
//获取字段的数据类型
Class<?> fieldType = field.getType();
//获取到set方法对象
Method method = clazz.getMethod(methodName, fieldType);
//执行set方法
method.invoke(obj , value);
}
------------------------------
1.10 反射结合配置文件
编写bean.properties,配置对象的唯一标识及对象的全类名,根据这段配置创建一个对象
//需求:编写bean.properties,配置对象的唯一标识及对象的全类名,根据这段配置创建一个对象
Properties properties = new Properties();
//将bean.properties中的数据存储到inputStream中
InputStream inputStream =Demo11.class.getClassLoader().getResourceAsStream("bean.properties");
//将bean.properties中的数据绑定到了Properties中!
properties.load(inputStream);
//获取全类名
String className = properties.getProperty("bean01");
//根据上述全类名创建了一个对象!
Object obj = Class.forName(className).newInstance();
System.out.println(obj);
String className2 = properties.getProperty("bean02");
Object obj2 = Class.forName(className2).newInstance();
System.out.println(obj2);
- bean.properties
bean01=com.qfedu.bean.User
bean02=com.qfedu.bean.Banana
------------------------------
1.11 静态代理设计模式
强被代理类的功能
前提
1,代理类和被代理类实现同一个接口
2,代理类中持有被代理类的对象
3,在增强方法中使用被代理类对象调用方法
//1,自定义一个代理类(增强类)实现和被代理类(被增强类)相同的接口
public class UserDaoImplProxy implements UserDao{
//2,在代理类中声明被代理类的引用
private UserDao userDao ;
public UserDaoImplProxy(){
userDao = new UserDaoImpl();
}
@Override
public void addUser() {
//3,在代理类的方法中使用被代理类调用方法
System.out.println("权限校验");
userDao.addUser();
System.out.println("日志记录");
}
@Override
public void deleteUser() {
userDao.deleteUser();
}
@Override
public void updateUser() {
userDao.updateUser();
}
@Override
public void selectUser() {
userDao.selectUser();
}
}
---------------
- 特点
* 缺点:必须要重写被代理类接口的所有的方法(包括不需要增强的方法,增高了耦合性)
* 作用:增强被代理类的功能
* 特点:可以控制被代理类对象
------------------------------
1.12 装饰者设计模式
- 前提
1,代理类和被代理类实现同一个接口
2,代理类中持有被代理类的引用
3,在增强方法中使用被代理类对象调用方法
---------------
- 开发步骤
* 定义装饰类(增强类)实现和被装饰类(被增强类)相同的接口
* 在装饰类中声明被装饰类的引用
* 在装饰类的方法中,使用被装饰调用原方法
---------------
//1,定义装饰类(增强类)实现和被装饰类(被增强类)相同的接口
public class UserDaoWrapper implements UserDao {
// 2,在装饰类中声明被装饰类的引用
private UserDao userDao ;
public UserDaoWrapper(){
}
public UserDaoWrapper(UserDao userDao) {
this.userDao = userDao;
}
@Override
public void addUser() {
System.out.println("权限校验");
userDao.addUser();
System.out.println("日志记录");
}
@Override
public void deleteUser() {
userDao.deleteUser();
}
@Override
public void updateUser() {
userDao.updateUser();
}
@Override
public void selectUser() {
userDao.selectUser();
}
}
* 作用:在不侵入被装饰类源码的前提下,增强某个功能!
* 特点:不能控制被装饰类对象
* 缺点:需要重写接口中的所有方法,破坏了单一职责原则
------------------------------
1.13 Proxy动态代理
动态代理:在程序运行过程中产生的这个代理对象,而程序运行过程中产生对象其实就是我们
刚才反射讲解的内容,所以,动态代理其实就是通过反射来生成一个代理
基于接口的方法增强
- 方式一:定义类实现InvocationHandler接口
- 增强addUser方法
public static void main(String[] args) {
UserDao userDao = new UserDaoImpl();//被代理类对象
UserDao userDaoProxy = (UserDao) Proxy.newProxyInstance(
userDao.getClass().getClassLoader(),
userDao.getClass().getInterfaces(),
new MyInvocationHandler(userDao));
userDaoProxy.addUser();
userDaoProxy.deleteUser();
userDaoProxy.updateUser();
userDaoProxy.selectUser();
}
/**
* 增强代理类
*/
class MyInvocationHandler implements InvocationHandler{
//声明一个被代理类的引用
private UserDao userDao;
public MyInvocationHandler(UserDao userDao) {
this.userDao = userDao;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// Method method : 被代理类的原方法
// Object[] args :被代理类的方法的实际参数
//执行被代理类中的原方法
String methodName = method.getName();
Object returnValue = null;
if ("addUser".equals(methodName)) {
//只有addUser需要增强
System.out.println("权限校验");
returnValue = method.invoke(userDao, args);//执行被代理类中的原方法
System.out.println("日志记录");
} else {
//其他的方法不增强
method.invoke(userDao,args);//执行被代理类中的原方法
}
return returnValue;
}
}
------------------------------
- 方式二:使用InvocationHandler接口的匿名内部类对象
- 增强deleteUser方法
public static void main(String[] args) {
UserDao userDao = new UserDaoImpl();
//使用匿名内部类对象
UserDao userDaoProxy = (UserDao) Proxy.newProxyInstance(
userDao.getClass().getClassLoader(),
userDao.getClass().getInterfaces(),
new InvocationHandler() {//处理增强
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
String methodName = method.getName();
Object returnValue = null;
if ("deleteUser".equals(methodName)) {
System.out.println("权限校验");
returnValue = method.invoke(userDao, args);
System.out.println("日志记录");
} else {
returnValue = method.invoke(userDao, args);
}
return returnValue;
}
});
userDaoProxy.deleteUser();
userDaoProxy.addUser();
}
二 注解
2.1 注解介绍
一个修饰符
- 定义
注解annotation是Java语言中用于描述类,成员变量,构造方法,成员方法,方法参数及包声明的
特殊的修饰符.用于描述这些信息的元数据.例如@Override用于描述一个方法是在子类中重
写的方法
---------------
- 特点
是JDK5.0之后引入的特性.注解是以”@注解名”在代码中存在的
---------------
- 作用
跟踪代码依赖性
执行编译时格式检查
代替已有的配置文件
------------------------------
2.2 java内置注解
- @Overirde 标记指定方法是一个重写方法,否则报错
标记在成员方法上,用于标识当前方法是重写父类方法,编译器在对该方法进行编译时会检查
是否符合重写规则,如果不符合,编译报错
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
public @interface Override {
}
---------------
- @Deprecated 标记一个类、字段、方法是一个过时的
如果开发者调用了被标记为过时的方法,编译器在编译期进行警告
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(value={CONSTRUCTOR, FIELD, LOCAL_VARIABLE, METHOD, PACKAGE, PARAMETER, TYPE})
public @interface Deprecated {
}
---------------
- @SuppressWarings
可放置在类和方法上,该注解的作用是阻止编译器发出某些警告信息,该注解为单值注解,只有
一个value参数,该参数为字符串数组类型,参数值常用的有
@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE})
@Retention(RetentionPolicy.SOURCE)
public @interface SuppressWarnings {
String[] value();
}
unchecked 未检查的转化,如集合没有指定类型还添加元素
unused 未使用的变量
resource 有泛型未指定类型
path 在类路径,原文件路径中有不存在的路径
deprecation 使用了某些不赞成使用的类和方法
fallthrough switch语句执行到底没有break关键字
rawtypes 没有写泛型,比如: List list = new ArrayList();
all 全部类型的警告,用的最多是all
------------------------------
2.3 注解分类
- 根据注解的参数个数分为三类
标记注解:没有参数的注解,仅用自身的存在与否为程序提供信息,如@Override 注解,该注解
没有参数,用于表示当前方法为重写方法
单值注解:只有一个参数的注解,如果该参数的名字为value,那么可以省略参数名,如
@SuppressWarnings(“all”),可以简写为@SuppressWarnings(“all”).
完整注解:有多个参数的注解
------------------------------
2.4 元注解概述
作用在自定义注解上,规定自定义注解的作用区域、存活策略
---------------
2.4.1 常用的元注解
- @Target 规定自定义注解的作用区域
作用:用于描述当前定义的注解可以应用的范围 该注解仅有一个属性value,该属性值
为ElementType数组类型 ElementType为枚举类型,枚举值和作用说明如下:
TYPE 当前定义的注解可以应用在类、接口和枚举的类型定义部分
FILED 当前定义的注解可以应用在成员变量上
METHOD 当前定义的注解可以应用在成员方法上
PARAMETER 当前定义的注解可以应用在方法参数上
CONSTRUCTOR 当前定义的注解可以应用在构造方法上
LOCAL_VARIABLE 当前定义的注解可以应用在局部变量上
ANNOTATION_TYPE 当前定义的注解可以应用在注解类型上
PACKAGE 当前定义的注解可以应用在包定义语句上
---------------
- @Retention 规定自定义注解的存活策略
作用:用于描述当前定义的注解可以保留的时间长短 该注解只有一个value参数,参数类型
为RetentionPolicy. RetentionPolicy类型为枚举类型
SOURCE 当前定义的注解仅仅停留在源码中,编译时即除去
CLASS 当前定义的注解保留到编译后的字节码中,运行时无法获取注解信息
RUNTIME 当前定义的注解可以保留到运行时,通过反射机制可以获取注解信息
------------------------------
2.5 自定义注解
- 格式
public @interface 注解名 {
数据类型 属性名1() default 默认值1;
数据类型 属性名2() ;
}
public @interface MyAnnotation01 {
String username() default "root";
String password() default "root123";
int age() default 16;
String value();
}
2.5.1 自定义注解@MyTest
测试类中,方法上如果使用了@MyTest注解,该方法就会执行
开发步骤
* 自定义注解@MyTest
* 在测试类Test01上使用@MyTest
* 让@MyTest注解生效
* 获取到Test01类对应的Class对象
* 获取Test01类中的所有方法
* 判断方法上是否有@MyTest注解
* 如果有,就将该方法执行
* 如果没有,就不处理
/**
* @MyTest注解
* @MyTest的存活策略必须是RUNTIME
*/
@Retention(RetentionPolicy.RUNTIME)
public @interface MyTest {
String value() default "";
}
/*
* Test01测试类
*/
public class Test01 {
@Test
public void test01(){
System.out.println("test01");
}
@MyTest
public void test02(){
System.out.println("test02");
}
@MyTest
public void test03(){
System.out.println("test03");
}
public void test04(){
System.out.println("test04");
}
}
/*
* demo类
*/
public class Demo02 {
public static void main(String[] args) {
//使用反射,扫描Test01类里面有哪些方法有@MyTest注解
//如果有@MyTest注解,就将起执行
//如果没有@MyTest注解,不做任何处理
//1,获取Test01类对应的Class对象
Class<Test01> clazz = Test01.class;
//2,获取Test01类下所有的方法对象
Method[] methods = clazz.getMethods();
//stream流 lambda表达式
Arrays.stream(methods).forEach(method -> {
//method就是单个方法对象
//3,判断方法上是否有@MyTest注解
boolean present = method.isAnnotationPresent(MyTest.class);
if (present) {
//方法上有@MyTest注解,执行方法
try {
method.invoke(clazz.newInstance());
} catch (Exception e) {
e.printStackTrace();
}
} else {
//方法上没有@MyTest注解
}
});
}
}
2.5.2 自定义注解@JDBCInfo
用注解@JDBCInfo替代jdbc.properties配置文件
开发步骤
* 自定义注解@JDBCInfo
* 在JDBCUtils工具类上使用@JDBCInfo
* 在JDBCUtils工具类中静态代码块中,获取@JDBCInfo注解上的属性,并给JDBCUtils工具类中成员变量赋值
/*
* 自定义注解@JDBCInfo
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface JDBCInfo {
String driverClass() default "com.mysql.jdbc.Driver";
String jdbcUrl() default "jdbc:mysql://localhost:3306/day54";
String user() default "root";
String password() default "123465";
}
/*
* 在JDBCUtils工具类上使用@JDBCInfo
* 反射读取注解@JDBCInfo中的内容
*/
@JDBCInfo(password = "root")
public class JDBCUtils {
private static final String DRIVERCLASS ;
private static final String JDBCURL;
private static final String USER;
private static final String PASSWORD;
static {
Class<JDBCUtils> clazz = JDBCUtils.class;
boolean present = clazz.isAnnotationPresent(JDBCInfo.class);
if (present) {
//JDBCUtils类上有@JDBCInfo注解,获取该注解
JDBCInfo jdbcInfo = clazz.getAnnotation(JDBCInfo.class);
//从@JDBCInfo注解获取driverClass、jdbcUrl、user、password属性值
DRIVERCLASS = jdbcInfo.driverClass();
JDBCURL = jdbcInfo.jdbcUrl();
USER = jdbcInfo.user();
PASSWORD = jdbcInfo.password();
} else {
//JDBCUtils类上没有@JDBCInfo注解,从properties文件
DRIVERCLASS = "";
JDBCURL = "";
USER = "";
PASSWORD = "";
}
}
}
------------------------------
2.6 反射、注解、设计模式
2.6.1 初级版
开发步骤
* 自定义注解@SystemLog
* className
* methodName
* 定义一个UserDao接口,且在该接口使用注解@SystemLog
* 编写装饰者设计模式
* 获取UserDao实现子类的Class对象
* 获取UserDao接口的Class对象
* 获取UserDao接口中的方法对象
/*
* 自定义注解@SystemLog
* 注解存活策略:RUNTIME
*/
@Retention(RetentionPolicy.RUNTIME)
public @interface SystemLog {
String className();//记录类名
String methodName();//记录方法名
}
/*
* 定义一个UserDao接口,且在该接口使用注解@SystemLog
* 设置@SystemLog中className属性、methodName属性
*/
public interface UserDao {
@SystemLog(className = "com.qfedu.dao.UserDao" , methodName = "addUser")
void addUser() throws Exception;
void deleteUser() throws Exception;
@SystemLog(className = "com.qfedu.dao.UserDao" , methodName = "updateUser")
void updateUser() throws Exception;
}
/*
* 写装饰者设计模式
*/
public class UserDaoWrapper implements UserDao{
private UserDao userDao;
public UserDaoWrapper(UserDao userDao) {
this.userDao = userDao;
}
@Override
public void addUser() throws Exception {
userDao.addUser();
printLog("addUser");
}
@Override
public void deleteUser() throws Exception {
userDao.deleteUser();
printLog("deleteUser");
}
@Override
public void updateUser() throws Exception {
userDao.updateUser();
printLog("updateUser");
}
/**
* 日志记录
* @param runMethodName
*/
private void printLog(String runMethodName) throws Exception {
//判断接口上对应的方法中是否有@SystemLog注解
//获取UserDao接口实现子类的Class对象
Class<? extends UserDao> sonClazz = userDao.getClass();
//获取UserDao接口的Class对象
Class<?>[] interfaces = sonClazz.getInterfaces();
Class<?> fatherClazz = interfaces[0];
//获取接口中对应的方法对象(addUser方法)
Method method = fatherClazz.getMethod(runMethodName);
if (null != method) {
//判断方法上是否有@SystemLog注解
boolean present = method.isAnnotationPresent(SystemLog.class);
if (present) {
//方法有@SystemLog注解,打印日志
SimpleDateFormat format = new SimpleDateFormat("yyyy年MM月dd日 hh:mm:ss");
Date currentDate = new Date();
String currentTimeStr = format.format(currentDate);
SystemLog systemLog = method.getAnnotation(SystemLog.class);
String className = systemLog.className();
String methodName = systemLog.methodName();
System.out.println(currentTimeStr + " --- " + className + "类中 ---" + methodName + "()方法 --- 运行了");
}
}
}
}
public static void main(String[] args) throws Exception{
UserDao userDao = new UserDaoImpl();
UserDaoWrapper userDaoWrapper = new UserDaoWrapper(userDao);
userDaoWrapper.addUser();
userDaoWrapper.deleteUser();
userDaoWrapper.updateUser();
}
存在的问题
* 由装饰者设计模式的弊端导致每个方法中都需要调用printLog方法
2.6.2 优化版
开发步骤
* 自定义注解@SystemLog
* className
* methodName
* 定义一个UserDao接口,且在该接口使用注解@SystemLog
* 动态代理
* 获取UserDao接口中的方法对象
/*
* 自定义注解 @SystemLog
*/
@Retention(RetentionPolicy.RUNTIME)
public @interface SystemLog {
String className();//记录类名
String methodName();//记录方法名
}
/*
* 定义一个UserDao接口,且在该接口使用注解@SystemLog
* 设置@SystemLog中className属性、methodName属性
*/
public interface UserDao {
@SystemLog(className = "com.qfedu.dao.UserDao" , methodName = "addUser")
void addUser() throws Exception;
void deleteUser() throws Exception;
@SystemLog(className = "com.qfedu.dao.UserDao" , methodName = "updateUser")
void updateUser() throws Exception;
}
/*
* 动态代理
*/
public static void main(String[] args) {
UserDao userDao = new UserDaoImpl();
UserDao userDaoProxy = (UserDao) Proxy.newProxyInstance(
userDao.getClass().getClassLoader(),
userDao.getClass().getInterfaces(),
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//获取到接口中方法对象 , 比如 : UserDao接口中addUser方法
//之前
//先获取实现子类的Class对象
//再获取到接口的Class对象
//再获取到接口中的方法对象
//现在
//再获取到接口的Class对象 -- userDao.getClass().getInterfaces(),
//再获取到接口中的方法对象 -- Method method
//method : 就是接口中的方法对象
Object returnValue = null;
if (null != method) {
boolean present = method.isAnnotationPresent(SystemLog.class);
if (present) {
//如果有@SystemLog注解 , 执行原有功能 , 打印日志
returnValue = method.invoke(userDao, args);
String currentTimeStr = new SimpleDateFormat("yyyy年MM月dd日 hh:mm:ss") .format(new Date());
SystemLog systemLog = method.getAnnotation(SystemLog.class);
String className = systemLog.className();
String methodName = systemLog.methodName();
System.out.println(currentTimeStr + " --- " + className + "类中 ---" + methodName + "()方法 --- 运行了");
} else {
//如果没有@SystemLog注解, 执行原有功能, 不打印日志
returnValue = method.invoke(userDao, args);
}
}
return returnValue;
}
});
userDaoProxy.addUser();
}