Spring源码学习

前置学习

1.自定义注解

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Rege {

	public String value() default "";

	public int age() default 0;
}

@Retention来定义注解的生命周期

RUNTIME 正常我们自定义的注解就是runtime,因为在程序运行时候需要使用

SOURCE 这个只是一个资源标识,不会参与编译,比如说@Override

CLASS 只会编译到class文件中不会,参与运行。

@Target 来定义作用域,正常就是类、方法、字段,如果不加为都可以。
注解是继承的方式,所以自定义的注解上面可以添加多个其它注解,加上一个自定义注解,相当于都添加了。

public class TestRegeDTO {
	@Rege(value = "rege",age = 26)
	public String getIndex(){
		return "empty";
	}
	public static void main(String[] args) {
		Class<TestRegeDTO> testRegeDTOClass = TestRegeDTO.class;
		try {
			Method method = testRegeDTOClass.getDeclaredMethod("getIndex");
			if (method.isAnnotationPresent(Rege.class)) {
				Rege annotation = method.getAnnotation(Rege.class);
				int age = annotation.age();
				String value = annotation.value();
				System.out.println("value : " + value + " ; age : " + age);
			}
			method.getAnnotationsByType(Rege.class);
		} catch (NoSuchMethodException e) {
			e.printStackTrace();
		}
	}
}

@Rege(value = “rege”,age = 26) 注解如果只有一个value属性,可以省略value声明,例如:@Rege( “rege”)

注解的原理

注解本质上是继承了 Annotation 接口的接口,而当你通过反射,也就是我们这里的 getAnnotation 方法去获取一个注解类实例的时候,其实 JDK 是通过动态代理机制生成一个实现我们注解(接口)的代理类。

java源码中常用注解:

@Override:它没有任何的属性,所以并不能存储任何其他信息。它只能作用于方法之上,编译结束后将被丢弃。

@SuppressWarnings: 主要用来压制 java 的警告,它有一个 value 属性需要你主动的传值,而如果我们不希望程序启动时,编译器检查代码中过时的方法,就可以使用 @SuppressWarnings 注解并给它的 value 属性传入一个参数值来压制编译器的检查。

@Deprecated:过时方法。

2.JDK动态代理

动态代理的本质:生成一个代理对象,对类和方法做增强。这个代理对象才是目标对象。

为什么要使用动态代理来实现呢?:设计模式的单一职责的原则,而且不影响原来的业务逻辑。

静态代理:用过自己的编码写个子类来继承原来的类,然后手动在继承的方法前后来做增强,当然要实现的话,就需要来实现这个子类来执行业务逻辑。

静态代理缺点:1.会产生很多的代理类;2.产生的代理类只能代理既定接口

代码案例

public interface UserService {
	public String query(int id,String name);

	public void del();
}

public class UserServiceImpl implements UserService {
	@Override
	public String query(int id, String name) {
		log.debug("id:{}",id);
		log.debug("query database-----------logic");
		return name;
	}

	@Override
	public void del() {
		log.debug("del mysql----logic");
	}
}

public class LogJdkInvocationHandler implements InvocationHandler {
	Object target;

	public LogJdkInvocationHandler(Object target){
		this.target=target;
	}
	@Override
	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
		log.debug("jdk---start log-----proxy");

		Object invoke = null;
		try {
			invoke = method.invoke(target, args);
		} catch (IllegalAccessException e) {
			e.printStackTrace();
		} catch (InvocationTargetException e) {
			e.printStackTrace();
		}

		return invoke;
	}
}
public class TestService {
	public static void main(String[] args) {
		InvocationHandler invocationHandler = new LogJdkInvocationHandler(new UserServiceImpl());
		UserService service = (UserService) Proxy.newProxyInstance(TestService.class.getClassLoader(),
				new Class[]{UserService.class},
				invocationHandler);
		service.del();
	}
}

* Proxy.newProxyInstance的步骤 * 1、他会产生一段字符串 代理类的源码 * 2、把这个字符串输出到一个.java($Proxy.java)文件当中 * 3、会把这个$Proxy.java动态编译他成为一个$Proxy.class * 4、会通过一个类加载器把这个$Proxy.class加载到JVM当中 * 5、Class.foranme("xxxx").newInstance 反射实例化这个对象 proxyObject

实际上jvm直接产生了字节码放入到了jvm中(1、2、3就一步)。

动态代理的要素:传入类加载器,目标对象的接口,代理逻辑(实现类);

生成的代理对象会继承Proxy,所以动态代理只能实现接口,而且不是类。

cglib动态代理和jdk动态代理的区别:

一、简单来说:

  JDK动态代理只能对实现了接口的类生成代理,而不能针对类

  CGLIB是针对类实现代理,主要是对指定的类生成一个子类,覆盖其中的方法(继承)

二、Spring在选择用JDK还是CGLiB的依据:

   (1)当Bean实现接口时,Spring就会用JDK的动态代理

   (2)当Bean没有实现接口时,Spring使用CGlib是实现

   (3)可以强制使用CGlib(在spring配置中加入<aop:aspectj-autoproxy proxy-target-class="true"/>)

三、CGlib比JDK快?

  (1)使用CGLib实现动态代理,CGLib底层采用ASM字节码生成框架,使用字节码技术生成代理类,比使用Java反射效率要高。唯一需要注意的是,CGLib不能对声明为final的方法进行代理,因为CGLib原理是动态生成被代理类的子类。

  (2)在对JDK动态代理与CGlib动态代理的代码实验中看,1W次执行下,JDK7及8的动态代理性能比CGlib要好20%左右。