简单理解:注解可以理解为一个标注,编译器编译时,扫描到有标注时会作对应的处理。
2.在jdk包中的位置:@Target表示注解可以用在哪些地方@Retention表示注解在什么地方有效(runtime>class>sources)@documented表示是否将注解生成在JAVAdoc中@Inherited表示子类可以继承父类的注解
@Target的参数
public enum ElementType { TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR,
LOCAL_VARIABLE, ANNOTATION_TYPE, PACKAGE, TYPE_PARAMETER, TYPE_USE, MODULE
}
@Retention的参数
public enum RetentionPolicy { SOURCE, CLASS, RUNTIME }
3.自定义注解
为自定义的注解增加@Target和@Retention注解。
也可以接收参数。下面String value() default "注解";表示MyAnnotation可以接收一个String类型,名为value的参数;当没有接收到参数时,value默认值为"注解"。
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@interface MyAnnotation{
String value() default "注解";
}
4.注解的本质
以 上面的自定义注解MyAnnotation进行分析:
Class[] interfaces = MyAnnotation.class.getInterfaces();
for (Class inter : interfaces) {
System.out.println(inter);
}
//输出:interface java.lang.annotation.Annotation
System.out.println(MyAnnotation.class);
//输出:interface com.cong.test.MyAnnotation
可见:注解是一个接口,而Annotation也是一个接口;所有注解都继承了Annotation接口。
下面,看一下注解在运行时是怎么起作用的。
public class Test {
@MyAnnotation
public String name;
public static void main(String[] args) throws NoSuchFieldException {
Class clazz = Test.class;
Field field = clazz.getDeclaredField("name");
MyAnnotation annotation = field.getAnnotation(MyAnnotation.class);
System.out.println(annotation.value());
}
}
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@interface MyAnnotation{
String value() default "注解";
}
上图可以看到,注解在运行中会生成一个代理对象。可以通过此对象利用反射获得注解的所有信息。
System.out.println(new Test().name); //输出:null
注解在这里是一个标识,现在起不了作用,怎么能够实现一些功能?例如:让MyAnnotation为name赋值?
@ToString
public class Test {
@MyAnnotation("0.0")
public String name;
@MyAnnotation("3")
private int age;
public static void main(String[] args) throws Exception {
Test test = new Test();
for (Field declaredField : Test.class.getDeclaredFields()) {
MyAnnotation annotation = declaredField.getAnnotation(MyAnnotation.class);
if (annotation != null) {
String annotationValue = annotation.value();
//暴力反射,private也可以直接获取到
declaredField.setAccessible(true);
if (declaredField.getType().getName().equals("int")) {
int val = Integer.parseInt(annotationValue);
declaredField.set(test, val);
} else {
declaredField.set(test, annotationValue);
}
}
}
System.out.println(test);
}
}
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@interface MyAnnotation{
String value() default "注解";
}
输出:Test(name=0.0, age=3)
可见,注解结合反射,就可以起到想要的作用! 二、反射 1.Java反射机制功能:
在运行时判断任意一个对象所属的类在运行时构造任意一个类的对象在运行时判断任意一个类所具有的成员变量的方法在运行时获取泛型信息在运行时调用任意一个对象的成员变量和方法在运行时处理注解生成动态代理 2.反射操作:
public class ReflectTest {
public static void main(String[] args) throws Exception {
Class c = Class.forName("com.cong.test.Cat");
//1.创建对象,本质是调用类的无参构造器
Cat cat1 = (Cat) c.getDeclaredConstructor().newInstance();
System.out.println(cat1);
//2.通过有参构造器创建对象
Cat cat2 = (Cat) c.getDeclaredConstructor(String.class, int.class)
.newInstance("喵喵", 1);
System.out.println(cat2);
//3.通过反射获取一个方法
Method shout = c.getDeclaredMethod("shout", String.class);
shout.invoke(cat2, "喵喵");
//4.操作属性
Field id = c.getDeclaredField("id");
id.setAccessible(true); //私有属性需要关闭程序的安全检测,降低效率
id.set(cat2,2);
System.out.println(cat2);
}
}
@NoArgsConstructor
@AllArgsConstructor
@ToString
@Data
class Cat {
private String name;
private int id;
public void shout(String word) {
System.out.println("猫在说:" + word);
}
}
3.静态代理
UserDao:接口
public interface UserDao {
public void add();
public void delete();
}
UserDaoImpl:真实类
public class UserDaoImpl implements UserDao{
@Override
public void add() {
System.out.println("add()");
}
@Override
public void delete() {
System.out.println("delete()");
}
}
UserDaoProxy:代理类——一般会做一些附属操作
public class UserDaoProxy implements UserDao{
UserDaoImpl userDao;
public UserDaoProxy(UserDaoImpl userDao) {
this.userDao = userDao;
}
@Override
public void add() {
System.out.println("====>UserDaoProxy代理类开始");
userDao.add();
System.out.println("====>UserDaoProxy代理类结束");
}
@Override
public void delete() {
System.out.println("====>UserDaoProxy代理类开始");
userDao.delete();
System.out.println("====>UserDaoProxy代理类结束");
}
}
Test:测试
public class Test {
public static void main(String[] args) {
UserDaoProxy userDaoProxy = new UserDaoProxy(new UserDaoImpl());
userDaoProxy.add();
userDaoProxy.delete();
}
}
代理模式的好处:
可以使真实类专注于自己的业务,添加的功能由代理类进行!方便管理与修改!
坏处:
一个真实类就会产生一个代理类,代码量翻倍! 4.动态代理
分为两类:基于接口的动态代理、基于类的动态代理
基于接口:JDK动态代理基于类:cglib (1)JDK动态代理
UserDao
public interface UserDao {
public void add();
public String search(int id);
}
UserDaoImpl
public class UserDaoImpl implements UserDao {
@Override
public void add() {
System.out.println("add()");
}
@Override
public String search(int id) {
System.out.println("search(" + id + ")");
return "123456";
}
}
ObjectJDKProxy
public class ObjectJDKProxy implements InvocationHandler {
Object object;
public ObjectJDKProxy(Object o) {
this.object = o;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if (method.getName().equals("add")) {
System.out.println("=====>add()执行前");
Object res = method.invoke(object, args);
System.out.println("=====>add()执行后");
} else if (method.getName().equals("search")) {
System.out.println("=====>search()执行前");
Object res = method.invoke(object, args);
System.out.println("=====>search()执行后");
return res;
}
return null;
}
}
Test
public class Test {
public static void main(String[] args) {
Class[] interfaces = {UserDao.class};
UserDao userDao = (UserDao) Proxy.newProxyInstance(Test.class.getClassLoader(),
interfaces,new ObjectJDKProxy(new UserDaoImpl()));
userDao.add();
String res = userDao.search(3);
System.out.println(res);
}
}
可以通过 ObjectJDKProxy 类(继承于InvocationHandler)来代理其他需要代理的类,在 public Object invoke(...)中实现代理的功能既可!动态代理本质上是反射的应用!通过反射获取类的各种信息,并完成类的创建,在调用类的方法前后均可以加入要增加的功能。 (2)CGLIB动态代理
UserDao
public class UserDao {
public void add() {
System.out.println("调用了add方法");
}
}
CGLIBProxy
public class CGLIBProxy implements MethodInterceptor {
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("方法前");
Object res = methodProxy.invokeSuper(o, objects);
System.out.println("方法后");
return res;
}
}
CGLIBTest
public class CGLIBTest {
public static void main(String[] args) {
Enhancer enhancer = new Enhancer();
enhancer.setCallback(new CGLIBProxy());
UserDao userDao = (UserDao) enhancer.create();
userDao.add();
}
}
5.AOP思想
面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。
本质上是动态代理!
@Configuration:表名这是一个配置类,相当于application.xml@Bean:注册一个bean,相当于application.xml中的一个bean标签(方法的名字,相当于bean标签中的id)@Component:说明这个类被Spring接管,注册到了容器中@Value:属性值注入@Autowired:自动装配,byType方式装配@ComponentScan:指定要扫描的包,这个包下的注解就会生效 2.@Configuration使用
@Configuration //表明为配置类
@ComponentScan("com.cong.pojo") //扫描pojo包
@import(MyimportSelector.class) //继承importSelector:配置类的加载
public class MyConfig {
@Bean //在Spring容器中注册一个类型为Person,对象为person的对象。
public Person person() {
return new Person();
}
}
3.测试
pojo包下的类:
Job
@AllArgsConstructor
@NoArgsConstructor
@Data
@ToString
@Component
public class Job {
@Value("计算机")
private String jobName;
@Value("20000")
private int jobPay;
}
Hobby
@AllArgsConstructor
@NoArgsConstructor
@Data
@ToString
@Component
public class Hobby {
@Value("girl")
private String hobbyName;
@Value("1")
private int hobbyId;
}
Person
@AllArgsConstructor
@NoArgsConstructor
@Data
@ToString
@Component
public class Person {
@Value("聪")
private String name;
@Value("3")
private int age;
@Autowired
private Job job;
@Autowired
private Hobby hobby;
}
config包下的配置类
# config.properties config1=com.cong.config.MyConfig1 config2=com.cong.config.MyConfig2四、SpringBoot自动装配
主程序
@SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
SpringBoot启动时,扫描并加载spring.factories中所有的路径对应的包,根据@ConditionalOnXXX判断条件是否成立(是否有导入对应的启动器),有的话则自动装配的类导入容器!



