------<a href="http://www.itheima.com" target="blank">Java培训、Android培训、iOS培训、.Net培训</a>、期待与您交流! -------
注解:向编译器传达信息
注解相当于某种标记,在程序中加了注解就等于加了某种标记,以后,javac编译器,开发工具和其他程序可以通过反射来了解你的类和各种元素上有无何种标记,看你有什么标记,就干相应的事。标记可以加在包,类,字段,方法,方法的参数及局部变量上。
注解生命周期:RetentionPolicy.SOURCE、RetentionPolicy.CLASS、RententionPolicy,RUNTIM;
分别对应:java源文件àclass文件à内存中的字节码
几个简单的注解:
@Override 重写注解,告诉你下面这个方法是从父类/接口继承过来的,需要你重写一次
@Deprecated 提示这个方法已经过时 ,例如:
@Deprecated
public static void sayHello(){ //这时这个sayHello方法就是个过时的方法了。编译时会提示过时
System.out.println("hi,黑马程序员");
@SuppressWarnings("deprecation") 不提示过时,有了这个注解,就不再提示程序中的过时方法了
元注解:就是加在注解上的注解,一般用的有
@Retention(RetentionPolicy.RUNTIME)用于控制生命周期(有三个时间点:java源文件,class文件,内存中字节码;分别对应了RetetionPolicy.SOURCE、RetetionPolicy.CLASS、RetetionPolicy.RUNTIME)。
常用的@Override是SOURCE,@Deprecated是RUNTIME,@SuppressWarnings是SOURCE
@Target(ElementType.METHOD) 表示被它注释的代表对...起作用,例如举例是表示对下面的方法起作用。括号里可填ElementType这个枚举类的各种值例如TYPE(类型),FIELD(字段),CONSYRUCTOR(构造方法)等等,基本涵盖所有组成程序的所有元素。
注解的定义与应用:
最简单的示例~
@interface A {//定义一个注解类
}
@A
class B {//B应用了注解类
}
class C {
B.class.isAnnotionPresent(A.class);//反射技术判断是否用了注解
A a = B.class.getAnnotion(A.class);//另一个类调用
}
举个正规的例子:
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import cn.itcast.day1.EnumTest;
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD,ElementType.TYPE})//元注解
public @interface ItcastAnnotation {
String color() default "blue";//缺省值是blue
String value();
//如果只有value一个属性,则其他类调用的时候就可以写为@AnnotationColor("red"), 省去等于,仅限value。//如果不只value一个属性,则别的属性要加 default
int[] arrayAttr() default {3,4,4};//数组属性
EnumTest.TrafficLamp lamp() default EnumTest.TrafficLamp.RED;//上一篇枚举的例子中的枚举
MetaAnnotation annotationAttr() default @MetaAnnotation("lhm");//注解类型的属性,定义缺省值用到 @MetaAnnotation()
}
调用:
import java.lang.reflect.Method;
import javax.jws.soap.InitParam;
//设置注解的属性
@ItcastAnnotation(annotationAttr=@MetaAnnotation("flx"),color="red",value="abc",arrayAttr=1)
public class AnnotationTest {
@SuppressWarnings("deprecation")
@ItcastAnnotation("xyz")
public static void main(String[] args) throws Exception{
// TODO Auto-generated method stub
System.runFinalizersOnExit(true);
//此方法本已过时,但有@SuppressWarnings("deprecation"),就不再提示
if(AnnotationTest.class.isAnnotationPresent(ItcastAnnotation.class)){
ItcastAnnotation annotation = (ItcastAnnotation)AnnotationTest.class.getAnnotation(ItcastAnnotation.class);
System.out.println(annotation.color());//red
System.out.println(annotation.value());//abc
System.out.println(annotation.arrayAttr().length);//3
System.out.println(annotation.lamp().nextLamp().name());//GREEN(具体方法在上一篇)
System.out.println(annotation.annotationAttr().value());//flx
}
}
}
代理:
要为已存在的多个具有相同接口的目标类的各个方法增加一些系统功能,例如:异常处理、日志、计算方法的运行时间、事务管理、等等。代理类的对象本身并不真正实现服务,而是通过调用目标类/委托类对象的相关方法提供服务。代理提供对另一个对象的访问,同时隐藏实际对象的具体事实。代理一般会实现它所表示的实际对象的接口。代理可以访问实际对象,但是延迟实现实际对象的部分功能,实际对象实现系统的实际功能,代理对象对客户隐藏了实际对象。客户不知道它是与代理打交道还是与实际对象打交道。
静态代理:由程序员创建或特定工具自动生成源代码,再对其编译。在程序运行前,代理类的.class文件就已经存在了。
动态代理:在程序运行时,运用反射机制动态创建而成。
用到代理的地方:
不允许直接访问某些类;对访问要做特殊处理等
动态代理的工作原理:
Client(客户端)调用代理,代理的构造方法接受一个InvocationHandler,client调用代理的各个方法,代理的各个方法请求转发给刚才通过构造方法传入的handler对象,又把各请求分发给目标的相应的方法。就是将handler封装起来,其中this引用了当前的放(发来什么请求就接受哪个方法)。
AOP:
系统中存在交叉业务,一个交叉业务就是要切入到系统中的一个方面,如下所示:
StudentService ------|----------|------------|-------------
CourseService ------|----------|------------|-------------
MiscService ------|----------|------------|-------------
安全,事务,日志等功能要贯穿到好多个模块中,所以,它们就是交叉业务
用具体的程序代码描述交叉业务:
method1 method2 method3
{ { {
------------------------------------------------------切面
.... .... ......
------------------------------------------------------切面
} } }
交叉业务的编程问题即为面向方面的编程(Aspect oriented program ,简称AOP),AOP的目标就是要使交叉业务模块化。可以采用将切面代码移动到原始方法的周围,这与直接在方法中编写切面代码的运行效果是一样的,如下所示:
------------------------------------------------------切面
func1 func2 func3
{ { {
.... .... ......
} } }
------------------------------------------------------切面
使用代理技术正好可以解决这种问题,代理是实现AOP功能的核心和关键技术。
代理的简单示例
目标类 doSomeThing(){
代理类 doSomeThing(){
例子:
import java.lang.reflect.Method;
public interface Advice {//定义一个Advice接口
void beforeMethod(Method method);
void afterMethod(Method method);
}
public class MyAdvice implements Advice {//类MyAdvice是接口Advice的实现
long beginTime = 0;
public void afterMethod(Method method) {
// TODO Auto-generated method stub
System.out.println("从黑马程序员毕业上班啦!");
long endTime = System.currentTimeMillis();
System.out.println(method.getName() + " running time of " + (endTime - beginTime));
}
public void beforeMethod(Method method) {
// TODO Auto-generated method stub
System.out.println("到黑马程序员来学习啦!");
beginTime = System.currentTimeMillis();
}
}
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.ArrayList;
import java.util.Collection;
public class ProxyTest {
public static void main(String[] args) throws Exception{
// TODO Auto-generated method stub
//1.创建一个实现了Collection接口的动态代理类
//获取代理类Proxy的字节码文件对象,传入classloader、接口类的字节码文件
Class clazzProxy1 = Proxy.getProxyClass(Collection.class.getClassLoader(), Collection.class);
System.out.println(clazzProxy1.getName());//动态类名:$Proxy0
//2.列出动态类的所有构造方法和参数签名
System.out.println("----------begin constructors list----------");
Constructor[] constructors = clazzProxy1.getConstructors();
for(Constructor constructor : constructors){
String name = constructor.getName();//$Proxy0
StringBuilder sBuilder = new StringBuilder(name);
sBuilder.append('(');
//遍历构造函数的参数,添加进字符串缓冲区中
Class[] clazzParams = constructor.getParameterTypes();
for(Class clazzParam : clazzParams){
sBuilder.append(clazzParam.getName()).append(',');
}
if(clazzParams!=null && clazzParams.length != 0)
sBuilder.deleteCharAt(sBuilder.length()-1);
sBuilder.append(')');
//输出每个构造方法名称和参数列表
System.out.println(sBuilder.toString()); //$Proxy0(java.lang.reflect.InvocationHandler)
}
//3.列出动态类的所有方法和参数签名
System.out.println("----------begin methods list----------");
Method[] methods = clazzProxy1.getMethods();
for(Method method : methods){
String name = method.getName();
StringBuilder sBuilder = new StringBuilder(name);
sBuilder.append('(');
Class[] clazzParams = method.getParameterTypes();
for(Class clazzParam : clazzParams){
sBuilder.append(clazzParam.getName()).append(',');
}
if(clazzParams!=null && clazzParams.length != 0)
sBuilder.deleteCharAt(sBuilder.length()-1);
sBuilder.append(')');
//将每个方法名和参数列表打印出来
System.out.println(sBuilder.toString());
}
//4.创建动态类的实例对象
System.out.println("----------begin create instance object----------");
//Object obj = clazzProxy1.newInstance();这里不能直接用Class创建对象,因为没有无参构造器
//创建参数类型实例,传入类的有参构造
Constructor constructor = clazzProxy1.getConstructor(InvocationHandler.class);
class MyInvocationHander1 implements InvocationHandler{
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
// TODO Auto-generated method stub
return null;
}
}
Collection proxy1 = (Collection)constructor.newInstance(new MyInvocationHander1());
System.out.println(proxy1);//null
proxy1.clear();
//proxy1.size();
//System.out.println("111111111111111");
//创建方法二:直接在代理类的有参构造器中传入参数对象
Collection proxy2 = (Collection)constructor.newInstance(new InvocationHandler(){
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
return null;
}
});
//创建方法三:通过代理类Proxy的newProxyInstance方法直接创建对象
final ArrayList target = new ArrayList(); //被代理的对象,实现了Collection的集合
//传入目标对象、系统功能代码,调用getProxy,生成动态代理对象
Collection proxy3 = (Collection)getProxy(target,new MyAdvice());
//测试,用动态代理类调用目标对象方法
proxy3.add("zxx");
proxy3.add("lhm");
proxy3.add("bxd");
System.out.println(proxy3.size());
System.out.println(proxy3.getClass().getName());
}
//将目标和系统功能代码抽出封装成一个getProxy方法
private static Object getProxy(final Object target,final Advice advice) {
Object proxy3 = Proxy.newProxyInstance(
//定义代理类的类加载器
//调用newProxyInstance方法时必须与目标类的类加载器和接口一致
//类A调用了类B,那么B的类加载器就要用A的
target.getClass().getClassLoader(),
//代理类要实现的接口列表
target.getClass().getInterfaces(),
//传入构造函数调用的InvocationHandle类对象
new InvocationHandler(){
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
advice.beforeMethod(method);
Object retVal = method.invoke(target, args);
advice.afterMethod(method);
return retVal;
}
}
);
return proxy3;
}
}