自定义注解与设计模式

一、自定义注解

  1. 什么是注解
    注解是JDK1.5新增的技术(主要有:注解、泛型、并发编程用的并发包)作用是提高编程效率。很多框架为了简化代码,都会提供有些注解,如springboot的微服务架构就摒弃了xml配置使用注解实现配置。注解可以理解为插件,是代码级别的插件,在类的方法上写:@XXX,就是在代码上插入了一个插件。注解不会也不能影响代码的实际逻辑,仅仅起到辅助性的作用。
    为什么要理解注解实现原理?因为在企业级开发过程中,架构师角色一般会针对代码进行重构(运用设计模式、自定义注解等),让别人使用注解,更方便高效的进行开发工作。
  2. 注解的分类
    1. 内置注解(jdk自带的注解,也称为元注解)
      如:@SuppressWarnings,对有警告的代码前面加上,可以在javac编译中去除警告
      @Deprecated,表示带有标记的包,方法,字段已过时
      @Overricle,加上这个标记,说明该方法是将父类的方法重写
    2. 自定义注解(Spring框架的注解)
      元注解的作用就是负责注解其他注解。Java5.0定义了4个标准的meta-annotation类型,它们被用来提供对其它 annotation类型作说明。Java5.0定义的元注解
      1. @Target
        @Target说明了Annotation所修饰的对象范围:Annotation可被用于 packages、types(类、接口、枚举、Annotation类型)、类型成员(方法、构造方法、成员变量、枚举值)、方法参数和本地变量(如循环变量、catch参数)。在Annotation类型的声明中使用了target可更加明晰其修饰的目标。
        1.CONSTRUCTOR:用于描述构造器
        2.FIELD:用于描述域
        3.LOCAL_VARIABLE:用于描述局部变量
        4.METHOD:用于描述方法
        5.PACKAGE:用于描述包
        6.PARAMETER:用于描述参数
        7.TYPE:用于描述类、接口(包括注解类型) 或enum声明
      2. @Retention
        表示需要在什么级别保存该注释信息,用于描述注解的生命周期(即:被描述的注解在什么范围内有效
        RetentionPolicy的取值包含以下三种:
        SOURCE:源码级别保留,编译后即丢弃。
        CLASS:编译级别保留,编译后的class文件中存在,在jvm运行时丢弃,这是默认值。
        RUNTIME: 运行级别保留,编译后的class文件中存在,在jvm运行时保留,可以被反射调用
      3. @Documented
        表明这个注解应该被 javadoc工具记录.,默认情况下javadoc是不包括注解的. 但如果声明注解时指定了 @Documented,则它会被 javadoc 之类的工具处理, 所有注解类型信息也会被包括在生成的文档中,是一个标记注解,没有成员。
      4. @Inherited
        @Inherited是一个标记注解,阐述了某个被标注的类型是被继承的。 如果一个使用了@Inherited修饰的annotation类型被用于一个class,则这个annotation将被用于该class的子类。
  3. ORM映射框架实现原理:
    数据库表采用驼峰式命名字段,如:user_name、user_age,而Java实体类中对应的属性名为:userName、userAge,例如在Hibernate框架中对属性加上@Column(“user_name”) 就能实现实体和数据库关系的映射,这个ORM框架实体类与表字段不一致,底层生成sql语句原理如下:
    1. 创建两个自定义注解,Table.java、Column.java。
      /**
       * @classDesc: 功能描述(表的别名)
       * @author: ChauncyWang
       * @version: 1.0
       */
      @Retention(RetentionPolicy.RUNTIME)
      public @interface Table{
      	String value();
      }
      /**
       * @classDesc: 功能描述(属性注解)
       * @author: ChauncyWang
       * @version: 1.0
       */
      @Retention(RetentionPolicy.RUNTIME)
      public @interface Column{
      	String name();
      	int length();
      }
      
    2. 创建实体类UserEntity .java
      //一个类可以使用多个注解
      @Table("user")
      public class UserEntity {
      	@Column(name = "user_name", length = 10)
      	private String userName;
      	@Column(name = "user_age", length = 8)
      	private Integer age;
      	public String getUserName() {
      		return userName;
      	}
      	public void setUserName(String userName) {
      		this.userName = userName;
      	}
      	public Integer getAge() {
      		return age;
      	}
      	public void setAge(Integer age) {
      		this.age = age;
      	}
      }
      
    3. 创建ORM底层实现类Reflex.java
      /**   
       * @classDesc: 功能描述(使用反射技术获取自定义注解值,实现orm框架的底层原理)  
       * @author: ChauncyWang
       * @version: 1.0  
       */ 
      public class Reflex {
      	
      	public static void main(String[] args) throws ClassNotFoundException {
      		// 1.项目使用注解,肯定会使用到反射。反射应用场景 jdbc、springIOC、常用框架、一些注解的底层实现
      		Class<?> forName = Class.forName("chauncy.customannotation.UserEntity");
      		/*
      		 * //getAnnotations()表示该类上用到了哪些注解,只表示类上的 
      		 * Annotation[] annotations = forName.getAnnotations(); 
      		 * for (Annotation annotation : annotations) {
      		 * 	 System.out.println(annotation); 
      		 * }
      		 */
      		Field[] declaredFields = forName.getDeclaredFields();
      		StringBuffer sf=new StringBuffer();
      		sf.append(" select ");
      		for (int i = 0; i < declaredFields.length; i++) {
      			Column column= declaredFields[i].getAnnotation(Column.class);
      			String columnName= column.name();
      			sf.append(columnName);
      			if(i== declaredFields.length-1){
      				sf.append(" from ");
      			}else {
      				sf.append(" , ");
      			}
      		}
      		// getAnnotation获取某个注解对象
      		Table table= forName.getAnnotation(Table.class);
      		//表的名称
      		String tableName = table.value();
      		sf.append(" "+ tableName );
      		//生成orm框架sql语句
      		System.out.println(sf.toString());
      	}
      }
      

二、常用设计模式

  1. 什么是设计模式?
    设计模式(Design pattern)是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结。使用设计模式是为了可重用代码、让代码更容易被他人理解、保证代码可靠性。 毫无疑问,设计模式于己于他人于系统都是多赢的,设计模式使代码编制真正工程化,设计模式是软件工程的基石,如同大厦的一块块砖石一样。项目中合理的运用设计模式可以完美的解决很多问题,每种模式在现在中都有相应的原理来与之对应,每一个模式描述了一个在我们周围不断重复发生的问题,以及该问题的核心解决方案,这也是它能被广泛应用的原因。
    通俗来说,设计模式就是前人针对特定问题的一套解决方案,或者说是套路。
  2. 为什么要使用设计模式?
    代理设计模式:打印请求、响应的日志
    工厂设计模式:负责初始化一些Bean
    模板方法: 重构
    观察者设计模式: 负责事件监听
    最终目的就是,提高代码的复用性(简化代码,跟自定义注解功能相似,都是简化代码,但是思想不一样,设计模式是面向对象的思想,接口、父类、抽象类都是为了设计模式)、健壮性,让别人能够易懂
  3. 设计模式的分类:
    总体来说设计模式分为三大类,23种设计模式
    创建型模式(主要对bean做一些操作,初始化之类的),共五种:工厂方法模式、抽象工厂模式、单例模式、建造者模式、原型模式。
    结构型模式(对象拥有什么样的结构进行管理),共七种:适配器模式、装饰器模式、代理模式、外观模式、桥接模式、组合模式、享元模式。
    行为型模式(对对象和方法做一些处理),共十一种:策略模式、模板方法模式、观察者模式、迭代子模式、责任链模式、命令模式、备忘录模式、状态模式、访问者模式、中介者模式、解释器模式。
    其实还有两类:并发型模式和线程池模式。
  4. 设计模式六大原则:
    1.开闭原则(Open Close Principle)
    开闭原则就是说对扩展开放,对修改关闭。在程序需要进行拓展的时候,不能去修改原有的代码,实现一个热插拔的效果。所以一句话概括就是:为了使程序的扩展性好,易于维护和升级。想要达到这样的效果,我们需要使用接口和抽象类,后面的具体设计中我们会提到这点。
    2.里氏代换原则(Liskov Substitution Principle)
    里氏代换原则(Liskov Substitution Principle LSP)面向对象设计的基本原则之一。 里氏代换原则中说,任何基类可以出现的地方,子类一定可以出现。 LSP是继承复用的基石,只有当衍生类可以替换掉基类,软件单位的功能不受到影响时,基类才能真正被复用,而衍生类也能够在基类的基础上增加新的行为。里氏代换原则是对“开-闭”原则的补充,实现“开-闭”原则的关键步骤就是抽象化,而基类与子类的继承关系就是抽象化的具体实现,所以里氏代换原则是对实现抽象化的具体步骤的规范
    3.依赖倒转原则(Dependence Inversion Principle)
    这个是开闭原则的基础,具体内容:针对接口编程,依赖于抽象而不依赖于具体
    4.接口隔离原则(Interface Segregation Principle)
    这个原则的意思是:使用多个隔离的接口,比使用单个接口要好。还是一个降低类之间的耦合度的意思,从这儿我们看出,其实设计模式就是一个软件的设计思想,从大型软件架构出发,为了升级和维护方便。所以上文中多次出现:降低依赖,降低耦合
    5.迪米特法则(最少知道原则)(Demeter Principle)
    为什么叫最少知道原则,就是说:一个实体应当尽量少的与其他实体之间发生相互作用,使得系统功能模块相对独立
    6.合成复用原则(Composite Reuse Principle)
    原则是尽量使用合成/聚合的方式,而不是使用继承
  5. 单例模式:
    1. 什么是单例?
      单例保证一个对象JVM(在一个jvm而不是多个jvm)中只能有一个实例,常见单例 懒汉式、饿汉式
      什么是懒汉式,就是需要的才会去实例化,线程不安全
      什么是饿汉式,就是当class文件被加载的时候,被初始化,天生线程安全
      相比来说懒汉式节约内存,但是懒汉式线程不安全,代码执行效率比饿汉式低
      为什么懒汉式比饿汉式代码执行效率低?
      因为懒汉式使用了synchronized加上了同步锁,所以饿汉式代码执行效率高
    2. 单例的代码实现:
      1. 懒汉式
        package chauncy.designpattern.singleton;
        
        import com.sun.swing.internal.plaf.synth.resources.synth;
        
        /**
         * 
         * @classDesc: 功能描述(保证Singleton这个类只能在jvm中有一个实例)
         * @author: ChauncyWang
         * @version: 1.0
         */
        class Singleton {
        
        	// 因为此类不能被实例化所以要变成static,静态的在堆内存中只能修饰一个
        	private static Singleton singleton;
        
        	/**
        	 * 如果把构造函数私有化,反射也不能初始化
        	 */
        	private Singleton() {
        
        	}
        
        	/**
        	 * @methodDesc: 功能描述(懒汉式:当需要的时候才会被实例化,之后都是一个实例。但是懒汉式线程不安全,可以使用同步锁解决线程不安全的问题,把容易出现线程不安全的代码包裹起来)
        	 * @author: ChauncyWang
        	 * @param: @return
        	 * @returnType: Singleton
        	 */
        	static public Singleton getSingleton() {
        		if (singleton == null) {
        			synchronized(Singleton.class){
        				singleton = new Singleton();
        			}
        		}
        		return singleton;
        	}
        }
        
        /**
         * @classDesc: 功能描述(懒汉式设计模式)
         * @author: ChauncyWang
         * @version: 1.0
         */
        public class SingletonIdler {
        	public static void main(String[] args) {
        		Singleton s1 = Singleton.getSingleton();
        		Singleton s2 = Singleton.getSingleton();
        		System.out.println(s1 == s2);
        
        	}
        }
        
      2. 饿汉式
        package chauncy.designpattern.singleton;
        
        //饿汉式是当class文件被加载的时候,就会被初始化执行,天生线程安全。
        class Singletonn{
        	private static final Singletonn singletonn=new Singletonn();
        	
        	private Singletonn(){
        		
        	}
        	
        	static public Singletonn getSingletonn() {
        		return singletonn;
        	}
        }
        
        /**   
         * @classDesc: 功能描述(饿汉式设计模式)  
         * @author: ChauncyWang 
         * @version: 1.0  
         */  
        public class SingletonHungry {
        	public static void main(String[] args) {
        		Singletonn s1 = Singletonn.getSingletonn();
        		Singletonn s2 = Singletonn.getSingletonn();
        		System.out.println(s1==s2);
        	}
        }
        

6.工厂模式:

  1. 什么是工厂模式?
    工厂设计模式就是实现创建者和调用者分离
  2. 工厂模式的代码实现:
    1. 简单工厂
      public class Audi implements Car{
      	@Override
      	public void run() {
      		System.out.println("我是奥迪。。。");
      	}	
      }
      public class Benz implements Car{	
      	@Override
      	public void run() {
      		System.out.println("我是奔驰。。。");
      	}
      }
      public interface Car {
      	//每个汽车都会有run,表示可以跑
      	public void run();	
      }
      public class CarFactory {
      	static public Car createCar(String carType){
      		//jdk1.7支持String类型switch
      		Car car=null;
      		switch (carType) {
      		case "奥迪":
      			car=new Audi();
      			break;
      		case "奔驰":
      			car=new Benz();
      			break;
      		default:
      			break;
      		}
      		return car;
      	}
      }
      /**   
       * @classDesc: 功能描述(简单工厂设计模式)  
       * @author: ChauncyWang
       * @version: 1.0  
       */  
      public class Main {
      	public static void main(String[] args) {
      		/*//传统创建对象的方式
      		Audi audi = new Audi();
      		Benz benz = new Benz();
      		audi.run();
      		benz.run();*/
      		//使用工厂创建对象的方式
      		Car car1 = CarFactory.createCar("奥迪");
      		Car car2 = CarFactory.createCar("奔驰");
      		car1.run();
      		car2.run();
      	}
      }
      
    2. 工厂方法
      public class Audi implements Car{
      	@Override
      	public void run() {
      		System.out.println("我是奥迪。。。");
      	}
      }
      public class Benz implements Car{
      	@Override
      	public void run() {
      		System.out.println("我是奔驰。。。");
      	}
      }
      public interface Car {
      	//每个汽车都会有run,表示可以跑
      	public void run();	
      }
      public class AudiFactory {
      	static public Car create(){
      		return new Audi();
      	}
      }
      public class BenzFactory {
      	static public Car create(){
      		return new Benz();
      	}
      }
      /**
       * @classDesc: 功能描述(工厂方法设计模式)
       * @author: ChauncyWang
       * @version: 1.0
       */
      public class Main {
      	public static void main(String[] args) {
      		Car audi = AudiFactory.create();
      		Car benz = BenzFactory.create();
      		audi.run();
      		benz.run();
      	}
      }
      

7.代理模式

  1. 什么是代理?
    通过代理控制对象的访问,可以详细访问某个对象的方法,在这个方法调用前处理,或调用后处理。既(AOP微实现) ,AOP核心技术面向切面编程。
    代理设计模式就是控制一个对象的访问,优点:保证安全性。
    SpringAOP里有方法的前置通知和后置通知,可以使用aop实现打印日志、权限判断、控制层打印请求响应。

  2. 代理应用场景:
    安全代理:可以屏蔽真实角色
    远程代理:远程调用代理类RMI(用的不多,只支持java语言,所以大多数都是http、webservice)
    延迟加载:先加载轻量级代理类,真正需要在加载真实

  3. 代理的分类:

    1. 静态代理(静态定义代理类):
      代码实现:

      1. 创建接口House.java
        package chauncy.designpattern.proxy.staticproxy;
        
        public interface House {
        	public void sell();
        }
        
      2. 创建接口实现类XiaoMing.java
        package chauncy.designpattern.proxy.staticproxy;
        
        public class XiaoMing implements House{
        
        	@Override
        	public void sell() {
        		System.out.println("我要卖房。。。。");
        	}
        }
        
      3. 创建静态代理类StaticProxy.java
        package chauncy.designpattern.proxy.staticproxy;
        	
        /**   
         * @classDesc: 功能描述(中介)  
         * @author: ChauncyWang 
         * @version: 1.0  
         */  
        public class StaticProxy implements House{
        	private XiaoMing xiaoMing;
        
        	public StaticProxy(XiaoMing xiaoMing){
        		this.xiaoMing=xiaoMing;
        	}
        	
        	@Override
        	public void sell() {
        		//方法之前,可以应用于日志的打印	
        		System.out.println("我是中介,你卖方的操作,我开始监听了!");
        		xiaoMing.sell();
        		//方法之后,可以应用于日志的打印
        		System.out.println("我是中介,你卖方的操作,我结束监听了!");
        	}
        	
        	public static void main(String[] args) {
        		StaticProxy proxy = new StaticProxy(new XiaoMing());
        		proxy.sell();
        	}
        	
        }
        
    2. 动态代理(动态生成代理类):
      代码实现:

      1. jdk动态代理:
        package chauncy.designpattern.proxy.dynamicproxy;
        
        import java.lang.reflect.InvocationHandler;
        import java.lang.reflect.Method;
        import java.lang.reflect.Proxy;
        
        import chauncy.designpattern.proxy.staticproxy.House;
        import chauncy.designpattern.proxy.staticproxy.XiaoMing;
        
        /**   
         * @classDesc: 功能描述(jdk动态代理)  
         * @author: ChauncyWang
         * @version: 1.0  
         */  
        public class JdkProxy implements InvocationHandler{
        	public Object target;
        	
        	public JdkProxy(Object target){
        		this.target=target;
        	}
        	
        	@Override
        	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        		System.out.println("我是房产中介。。使用jdk动态代理开始监听了!");
        		Object invoke = method.invoke(target, args);
        		System.out.println("我是房产中介。。使用jdk动态代理结束监听了!");
        		return invoke;	
        	}
        	
        	public static void main(String[] args) {
        		XiaoMing xiaoMing = new XiaoMing();
        		JdkProxy jdkProxy = new JdkProxy(xiaoMing);
        		//jdk动态代理通过反射机制生成代理类
        		House house = (House) Proxy.newProxyInstance(xiaoMing.getClass().getClassLoader(),xiaoMing.getClass().getInterfaces(), jdkProxy);
        		house.sell();
        	}
        }
        
      2. cglib动态代理:
        package chauncy.designpattern.proxy.dynamicproxy;
        
        import java.lang.reflect.Method;
        
        import chauncy.designpattern.proxy.staticproxy.House;
        import chauncy.designpattern.proxy.staticproxy.XiaoMing;
        import net.sf.cglib.proxy.Enhancer;
        import net.sf.cglib.proxy.MethodInterceptor;
        import net.sf.cglib.proxy.MethodProxy;
        
        /**   
         * @classDesc: 功能描述(cglib动态代理,cglib.jar依赖asm.jar。asm.jar专门用于生成字节码文件,做字节码类库操作)  
         * @author: ChauncyWang
         * @version: 1.0  
         */  
        public class CglibProxy implements MethodInterceptor{
        
        	@Override
        	public Object intercept(Object o, Method method, Object[] arr, MethodProxy methodProxy) throws Throwable {
        		System.out.println("我是房产中介。。使用cglib动态代理开始监听了!");
        		Object invokeSuper = methodProxy.invokeSuper(o, arr);
        		System.out.println("我是房产中介。。使用cglib动态代理结束监听了!");
        		return invokeSuper;
        	}
        	
        	public static void main(String[] args) {
        		CglibProxy cglibProxy = new CglibProxy();
        		//cglib动态代理使用asm框架生成代理类
        		Enhancer enhancer = new Enhancer();
        		enhancer.setSuperclass(XiaoMing.class);
        		enhancer.setCallback(cglibProxy);
        		House house = (House) enhancer.create();
        		house.sell();
        	}
        
        }
        
  4. 静态代理和动态代理有什么区别?
    静态代理需要自己去实现代理类,一个接口一个代理类,很复杂,动态代理包含很多框架(jdk动态代理、cglib等),不要自己去实现代理类。

  5. jdk动态代理与cglib动态代理的区别?
    jdk动态代理是由Java内部的反射机制来实现的,cglib动态代理底层则是借助asm框架来实现的。总的来说,反射机制在生成类的过程中比较高效,而asm在生成类之后的相关执行过程中比较高效(可以通过将asm生成的类进行缓存,这样解决asm生成类过程低效问题)。还有一点必须注意:jdk动态代理的应用前提,必须是目标类基于统一的接口。如果没有上述前提,jdk动态代理不能应用。
    :asm其实就是java字节码控制。
    SpringAOP底层用的是cglib动态代理,生成代理类的机制不是反射是使用asm来实现的。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值