01_注解介绍
- 概念
- 就是一个修饰符
- 特点
- 是JDK5.0之后引入的特性。以“@注解名”形式存在
- 作用
- 跟踪代码依赖性
- 执行编译时格式检查
- 代替已有的配置文件
02_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
@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE}) @Retention(RetentionPolicy.SOURCE) public @interface SuppressWarnings { String[] value(); }
-
value : 注解所压制的警告的类型
unchecked 未检查的转化,如集合没有指定类型还添加元素 unused 未使用的变量 resource 有泛型未指定类型 path 在类路径,原文件路径中有不存在的路径 deprecation 使用了某些不赞成使用的类和方法 fallthrough switch语句执行到底没有break关键字 rawtypes 没有写泛型,比如: List list = new ArrayList(); all 全部类型的警告
用的最多是all
-
03_注解分类
- 注解分类
- 标记注解
- 注解中没有属性
- 比如:@Override、@Deprecated
- 单值注解
- 注解中只有一个属性
- 比如:@SuppressWarings
- 完整注解
- 注解中有多个属性
- 标记注解
04_自定义注解
-
格式
public @interface 注解名 { 数据类型 属性名1() default 默认值1; 数据类型 属性名2() ; }
-
基本使用
public @interface MyAnnotation01 { String username() default "root"; String password() default "root123"; int age() default 16; String value(); }
-
注意事项
- value属性单独使用时,是可以省略"value="。
05_元注解概述
-
概述
- 作用在自定义注解上,规定自定义注解的作用区域、存活策略!
-
常用的元注解
-
@Target
-
规定自定义注解的作用区域
TYPE:类、接口、注解、枚举 FIELD:成员你变量 METHOD:成员方法 PARAMETER:形参 CONSTRUCTOR:构造器 LOCAL_VARIABLE:局部变量 ANNOTATION_TYPE:注解类型 PACKAGE:包
-
-
@Retention
-
规定自定义注解的存活策略
SOURCE : 仅存活在源码 CLASS : 存活在编译期 RUNTIME : 存活在运行时
-
06_元注解使用
-
-
@Target
- 定义
@Target(value = { ElementType.TYPE , ElementType.FIELD , ElementType.METHOD , ElementType.PARAMETER , ElementType.LOCAL_VARIABLE } ) public @interface MyAnnotation01 { String value() default "hello annotation"; }
- 使用
@MyAnnotation01 public class Demo01 { @MyAnnotation01 private int num = 1; @MyAnnotation01 public static void main(@MyAnnotation01 String[] args) { @MyAnnotation01 int num = 1; } }
-
@Retention
- 定义
@Retention(RetentionPolicy.RUNTIME) public @interface MyAnnotation01 { String value() default "hello annotation"; }
- 一般情况下,注解存活策略是RUNTIME,什么原因?
- 注解要起作用,必须结合反射使用,而反射是在程序运行时(RUNTIME)执行!
06_自定义注解@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注解 } }); } }
- @MyTest注解
07_自定义注解@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/day53"; String user() default "root"; String password() default "root123"; }
- 在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 = ""; } } }
08_反射、注解、设计模式初级版
-
开发步骤
- 自定义注解@SystemLog
- className
- methodName
- 定义一个UserDao接口,且在该接口使用注解@SystemLog
- 编写装饰者设计模式
- 获取UserDao实现子类的Class对象
- 获取UserDao接口的Class对象
- 获取UserDao接口中的方法对象
- 自定义注解@SystemLog
-
代码实现
-
自定义注解@SystemLog
- 注解存活策略:RUNTIME
@Retention(RetentionPolicy.RUNTIME) public @interface SystemLog { String className();//记录类名 String methodName();//记录方法名 }
-
定义一个UserDao接口,且在该接口使用注解@SystemLog
- 设置@SystemLog中className属性、methodName属性
public interface UserDao { @SystemLog(className = "com.dao.UserDao" , methodName = "addUser") void addUser() throws Exception; void deleteUser() throws Exception; @SystemLog(className = "com.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 + "()方法 --- 运行了"); } } } }
-
-
存在的问题
- 由装饰者设计模式的弊端导致每个方法中都需要调用printLog方法
09_反射、注解、设计模式优化版
-
开发步骤
- 自定义注解@SystemLog
- className
- methodName
- 定义一个UserDao接口,且在该接口使用注解@SystemLog
- 动态代理
- 获取UserDao接口中的方法对象
- 自定义注解@SystemLog
-
代码实现
-
自定义注解@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; }
-
动态代理
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();
-