Spring之自定义注解与设计模式

一、目标

  1. 熟悉注解底层原理
  2. 完成ORM框架底层原理
  3. 常用设计模式
  4. 单例、工厂、代理

二、自定义注解

1.1 什么是注解

Jdk1.5新增新技术,注解。很多框架为了简化代码,都会提供有些注解。可以理解为插件,是代码级别的插件,在类的方法上写:@XXX,就是在代码上插入了一个插件。

注解不会也不能影响代码的实际逻辑,仅仅起到辅助性的作用。

注解分类:内置注解(也成为元注解 jdk 自带注解)、自定义注解(Spring框架)

1.2 什么是内置注解

jdk自带的注解

比如:

  1. @SuppressWarnings 再程序前面加上可以在javac编译中去除警告–阶段是SOURCE
  2. @Deprecated 带有标记的包,方法,字段说明其过时----阶段是SOURCE
  3. @Overricle 打上这个标记说明该方法是将父类的方法重写–阶段是SOURCE

1.2.1 @Overricle 案例演示

@Override
public String toString() {
    return "User{" +
            "userId='" + userId + '\'' +
            ", userName='" + userName + '\'' +
            '}';
}

//源码
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
public @interface Override {
}

1.2.2 @ Deprecated案例演示

public void test(){
    //过时标志
    new Date().parse("");
}
//源码
@Deprecated
public static long parse(String s) {
    int year = Integer.MIN_VALUE;
    int mon = -1;
    int mday = -1;
    int hour = -1;
    int min = -1;
    int sec = -1;
    int millis = -1;
    int c = -1;
    int i = 0;
    int n = -1;
    int wst = -1;
    int tzoffset = -1;
    int prevc = 0;
}

1.2.3 @ SuppressWarnings 案例演示

//去除警告
@SuppressWarnings({"all"})
public void test(){
    //过时标志
    new Date().parse("");
    List objects = new ArrayList();
}

1.3 实现自定义注解

元注解的作用就是负责注解其他注解。

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

    表示需要在什么级别保存该注释信息,用于描述注解的生命周期(即:被描述的注解在什么范围内有效)

  3. @Documented

  4. @Inherited

/*
 注解@Retention可以用来修饰注解,是注解的注解,称为元注解
 */
//简化
//@Target(ElementType.METHOD)
/*
 当前注解作用范围
 */
@Target(value = {ElementType.METHOD,ElementType.TYPE})
/**
 * 1、RetentionPolicy.SOURCE:注解只保留在源文件,当Java文件编译成class文件的时候,注解被遗弃;
 * 2、RetentionPolicy.CLASS:注解被保留到class文件,但jvm加载class文件时候被遗弃,这是默认的生命周期;
 * 3、RetentionPolicy.RUNTIME:注解不仅被保存到class文件中,jvm加载class文件之后,仍然存在;
 * 这3个生命周期分别对应于:Java源文件(.java文件) ---> .class文件 ---> 内存中的字节码。
 */
@Retention(RetentionPolicy.RUNTIME)
/**
 * Documented注解表明这个注释是由 javadoc记录的,在默认情况下也有类似的记录工具。
 * 如果一个类型声明被注释了文档化,它的注释成为公共API的一部分。
 */
@Documented
/**
 * @Inherited注解标记其他的注解用于指明标记的注解是可以被自动继承的。
 * 此注解只对注解标记的超类有效,对接口是无效的。
 * 被元注解Inherited修饰的注解,只有作用在类上时,会被子类继承此自定义的注解,其余情况都不会继承
 * 也就是说注解和普通类的区别是如果一个子类想获取到父类上的注解信息,
 * 那么必须在父类上使用的注解上面 加上@Inherit关键字
 */
@Inherited
public @interface OneAnnotation {
    int beanId() default 0;
    String className() default "";
    String[] arrays();
}

1.4 实现ORM框架映射

完成案例,ORM框架实体类与表字段不一致,底层生成sql语句原理。

1.4.1 自定义表映射注解

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface SetTable {
    String value() default  "";
}

1.4.2 自定义字段属性

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface SetProperty {
    String name() default "";
    int length() default 20;
}

1.4.3 实体类

@SetTable("user")
public class User {
    @SetProperty(name = "user_id",length = 20)
    private String userId;
    @SetProperty(name = "user_name",length = 50)
    private String userName;
}

1.4.4 自定义注解代码实现

public class AnnotationDemo {

    public static void main(String[] args) throws ClassNotFoundException {
        //1.利用反射拿到实体对象
        Class<?> userClass = Class.forName("com.company.demo02.User");
        //2.获取表名称注解 取值
        SetTable setTable = userClass.getAnnotation(SetTable.class);
        //3.获取所有的成员属性
        StringBuffer sf = new StringBuffer();
        sf.append("select ");
        Field[] declaredFields = userClass.getDeclaredFields();
        for (int i = 0 ; i<declaredFields.length ; i++){
            Field field = declaredFields[i];
            //4.用成员属性 获取属性注解 取值
            SetProperty setProperty = field.getAnnotation(SetProperty.class);
            sf.append(setProperty.name());
            if(i==declaredFields.length-1){
                break;
            }
            sf.append(",");
        }
        //5.拼接sql
        sf.append(" from " +setTable.value());
        System.out.println(sf.toString());
    }
}

三、 常用设计模式

3.1 什么是设计模式

设计模式(Design pattern)是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结。
使用设计模式是为了可重用代码、让代码更容易被他人理解、保证代码可靠性。
设计模式是软件工程的基石,如同大厦的一块块砖石一样。
项目中合理的运用设计模式可以完美的解决很多问题,每种模式在现在中都有相应的原理来与之对应,每一个模式描述了一个在我们周围不断重复发生的问题,以及该问题的核心解决方案,这也是它能被广泛应用的原因。
本章系Java之美[从菜鸟到高手演变]系列之设计模式,我们会以理论与实践相结合的方式来进行本章的学习,希望广大程序爱好者,学好设计模式,做一个优秀的软件工程师!

3.2 设计模式的分类

总体来说设计模式分为三大类:

  1. 创建型模式

    1. 工厂方法模式
    2. 抽象工厂模式
    3. 单例模式
    4. 建造者模式
    5. 原型模式
  2. 结构型模式

    1. 适配器模式
    2. 装饰器模式
    3. 代理模式
    4. 外观模式
    5. 桥接模式
    6. 组合模式
    7. 享元模式
  3. 行为型模式

    1. 策略模式
    2. 模板方法模式
    3. 观察者模式
    4. 迭代子模式
    5. 责任链模式
    6. 命令模式
    7. 备忘录模式
    8. 状态模式
    9. 访问者模式
    10. 中介者模式
    11. 解释器模式
  4. 其实还有两类

    1. 并发型模式和线程池模式

在这里插入图片描述

3.3 设计模式的六大原则

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)

为什么叫最少知道原则,就是说:一个实体应当尽量少的与其他实体之间发生相互作用,使得系统功能模块相对独立。

2.4 单例模式

2.4.1 什么是单例模式?

单例保证一个对象JVM中只能有一个实例,常见单例 懒汉式、饿汉式

什么是懒汉式,

就是需要的才会去实例化,线程不安全

什么是饿汉式

就是当class文件被加载的时候,初始化,天生线程安全。

2.4.2 单例写法

懒汉式代码

/**
 * 懒汉式
 */
public class Singleton {
    // 当需要的才会被实例化
    private static Singleton singleton;
    //锁
    private static Object object = new Object();
    /**
     * 只有将构造函数私有化,才不可以new这个类,因为new一个类的时候是new的构造函数。
     *
     */
    private Singleton() {
    }
    /**
     * 构造函数私有化,外部类不可以new它,
     * 但是你需要写一个方法将你自己new的return 出去,如方法getInstance()。
     * @return
     */
    public  static Singleton getNewSingletion(){
        //第一次初始化
        if(singleton == null){
            //多个线程同时进入  需要同步代码块进行拦截
            synchronized(object){
                singleton = new Singleton();
            }
        }
        //其他 直接返回实例
        return singleton;
    }
}

饿汉式代码

//饿汉式代码
public class Singletion02 {
    //当class文件被加载初始化
    private static final  Singletion02 singletion02 = new Singletion02();

    private Singletion02() {
    }

    public static Singletion02 getSingleton(){
        return singletion02;
    }
}

测试入口

public class SingletionMain {
    public static void main(String[] args) {
        Singleton ns1 = Singleton.getNewSingletion();
        Singleton ns2 = Singleton.getNewSingletion();
        System.out.println(ns1==ns2);
        System.out.println("--------------------------------------------------------------------------------");
        Singletion02 singleton = Singletion02.getSingleton();
        Singletion02 singleton1 = Singletion02.getSingleton();
        System.out.println(singleton1==singleton);
    }
}

2.5 工厂模式

2.5.1 什么是工厂模式

实现创建者和调用者分离

2.5.2 简单工厂代码

准备实体

public interface Car {
    void run();
}
public class AoDi implements Car {
    @Override
    public void run() {
        System.out.println("奥迪跑车");
    }
}
public class FenTian implements Car {
    @Override
    public void run() {
        System.out.println("丰田跑车");
    }
}

创建工厂

public class CarFactory {
    public static Object createCar(String carName){
        Car car = null;
        switch (carName){
            case "奥迪":
                car = new AoDi();
                break;
            case "丰田":
                car = new FenTian();
                break;
        }
        return car;
    }
}

测试

public class FactoryDemo {
    public static void main(String[] args) {
        AoDi aodi =(AoDi) CarFactory.createCar("奥迪");
        FenTian fentian =(FenTian) CarFactory.createCar("丰田");
        aodi.run();
        fentian.run();
    }
}

2.5.3 工厂方法

工厂里包含方法

public interface Car {
    void run();
}
public class AoDi implements Car {
    @Override
    public void run() {
        System.out.println("奥迪跑车");
    }
}
public class FenTian implements Car {
    @Override
    public void run() {
        System.out.println("丰田跑车");
    }
}

奥迪工厂

//奥迪有多种系列
public interface AoDiFactory {
    static AoDi createAoDiA1(String name){
        AoDi aoDi = null;
        if("奥迪A1".equals(name)){
            aoDi= new AoDiA1();
        }
        return aoDi;
    }
}

//奥迪A1继承奥迪
public class AoDiA1 extends AoDi {
    @Override
    public void run() {
        super.run();
        System.out.println("奥迪A1");
    }
}

丰田工厂

public interface FenTionFactory {
    static FenTian createA1(String name){
        FenTian fenTian = null;
        if ("丰田A2".equals(name)){
            fenTian = new FengTianA2();
        }
        return fenTian;
    }
}
public class FengTianA2 extends FenTian {
    @Override
    public void run() {
        super.run();
        System.out.println("丰田A2");
    }
}
public class FactoryDemo {
    public static void main(String[] args) {
        AoDi aoDiA1 = AoDiFactory.createAoDiA1("奥迪A1");
        FenTian fenTianA2 = FenTionFactory.createA1("丰田A2");
        aoDiA1.run();
        fenTianA2.run();
    }
}

2.6 代理模式

2.6.1 什么是代理

通过代理控制对象的访问,可以详细访问某个对象的方法,在这个方法调用处理,或调用后处理。

好处;可以在目标对象实现的功能上,增加额外的功能补充,即扩展目标对象的功能.

符合了设计模式的开闭原则,即在对既有代码不改动的情况下进行功能的扩展。

既(AOP微实现) ,AOP核心技术面向切面编程。

在这里插入图片描述

2.6.2 代理应用场景

  1. 安全代理 可以屏蔽真实角色
  2. 远程代理 远程调用代理类RMI
  3. 延迟加载 先加载轻量级代理类,真正需要在加载真实

2.6.3 代理的分类

  1. 静态代理(静态定义代理类)
  2. 动态代理(动态生成代理类)
    1. Jdk 自带动态代理
    2. Cglib 、javaassist(字节码操作库)

2.6.4 静态代理

静态代理需要自己生成代理类

/*
    共同操作
 */
public interface Hose {
    //看房
    void see();
    //买房
    void mai();
}
//目标对象
public class XiaoMing implements Hose {
    @Override
    public void see() {
        System.out.println("小明:我要看房子");
    }
    @Override
    public void mai() {
        System.out.println("小明:我要买房子");
    }
}
//代理对象
public class ProxyDemo implements Hose {
    private XiaoMing xiaoMing;

    public ProxyDemo(XiaoMing xiaoMing) {
        this.xiaoMing = xiaoMing;
    }

    @Override
    public void see() {
        System.out.println("中介:看房子前需要准备钥匙");
        System.out.println("中介:准备好了");
        xiaoMing.see();
        System.out.println("中介:看房情况");
        System.out.println("中介:看房结束");
    }
    @Override
    public void mai() {
        System.out.println("中介:买房子前需要准备资料");
        System.out.println("中介:准备好了");
        xiaoMing.mai();
        System.out.println("中介:买房情况");
        System.out.println("中介:买房结束");
    }
}
//测试
public class ProxyMain {
    public static void main(String[] args) {
        Hose proxyDemo = new ProxyDemo(new XiaoMing());
        proxyDemo.see();
        proxyDemo.mai();
    }
}

2.6.5 JDK动态代理(不需要生成代理类)

其他不变,不用自己写代理类 实现InvocationHandler 就可以了

//jdk实现类
public class JDKProy implements InvocationHandler {
    private XiaoMing xiaoMing;

    public JDKProy(XiaoMing xiaoMing) {
        this.xiaoMing = xiaoMing;
    }
	/**
     *
     * @param proxy     动态生成的代理类实例
     * @param method    上文中实体类所调用的被代理的方法引用
     * @param args      参数值列表
     * @return
     * @throws Throwable
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        String name = method.getName();
        before(name);
        Object invoke = method.invoke(xiaoMing, args);
        after(name);
        return invoke;
    }

    public void after(String name){
        if("see".equals(name)){
            System.out.println("中介:看房情况");
            System.out.println("中介:看房结束");
        }else{
            System.out.println("中介:买房情况");
            System.out.println("中介:买房结束");
        }
    }
    public void before(String name){
        if("see".equals(name)){
            System.out.println("中介:看房子前需要准备钥匙");
            System.out.println("中介:准备好了");
        }else{
            System.out.println("中介:买房子前需要准备资料");
            System.out.println("中介:准备好了");
        }
    }
}
//测试
public class ProxyMain {
    public static void main(String[] args) {
        XiaoMing xiaoMing = new XiaoMing();
        JDKProy jdkProy = new JDKProy(xiaoMing);
        /*
        反射包  Proxy中的方法newProxyInstance 创建代理接口 需要目标类,目标类接口,jdk代理接口
        ClassLoader主要对类的请求提供服务,当JVM需要某类时,
        它根据名称向ClassLoader要求这个类,然后由ClassLoader返回 这个类的class对象。
         */
        //创建代理类
        Hose hose =(Hose) Proxy.newProxyInstance(xiaoMing.getClass().getClassLoader(),
                xiaoMing.getClass().getInterfaces(),
                jdkProy);
        hose.see();
        hose.mai();
    }
}

2.6.6 CGLIB动态代理

其他不变,代理实现类改变

public class CglibDemo implements MethodInterceptor {
    /**
     *
     * @param o             由CGLib动态生成的代理类实例
     * @param method        上文中实体类所调用的被代理的方法引用
     * @param objects       参数值列表
     * @param methodProxy   生成的代理类对方法的代理引用
     * @return
     * @throws Throwable
     */
    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        String name = method.getName();
        before(name);
        Object invoke = methodProxy.invokeSuper(o, objects);
        after(name);
        return invoke;
    }
    public void after(String name){
        if("see".equals(name)){
            System.out.println("中介:看房情况");
            System.out.println("中介:看房结束");
        }else{
            System.out.println("中介:买房情况");
            System.out.println("中介:买房结束");
        }
    }
    public void before(String name){
        if("see".equals(name)){
            System.out.println("中介:看房子前需要准备钥匙");
            System.out.println("中介:准备好了");
        }else{
            System.out.println("中介:买房子前需要准备资料");
            System.out.println("中介:准备好了");
        }
    }
}
//测试
public class ProxyMain {
    public static void main(String[] args) {
        CglibDemo cglibDemo = new CglibDemo();
        /*
        Enhancer类是CGLib中的一个字节码增强器,
        作用用于生成代理对象,Proxy类相似,常用方式为
         */
        Enhancer enhancer = new Enhancer();
        //将被代理类设置成父类 (就是目标类)
        enhancer.setSuperclass(XiaoMing.class);
        //设置拦截器   回调对象为本身对象
        enhancer.setCallback(cglibDemo);
        //动态生成一个代理类
        Hose hose = (Hose)enhancer.create();
        hose.see();
        hose.mai();
    }
}

2.6.7 CGLIB与JDK动态代理区别

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

static void main(String[] args) {
CglibDemo cglibDemo = new CglibDemo();
/*
Enhancer类是CGLib中的一个字节码增强器,
作用用于生成代理对象,Proxy类相似,常用方式为
*/
Enhancer enhancer = new Enhancer();
//将被代理类设置成父类 (就是目标类)
enhancer.setSuperclass(XiaoMing.class);
//设置拦截器 回调对象为本身对象
enhancer.setCallback(cglibDemo);
//动态生成一个代理类
Hose hose = (Hose)enhancer.create();
hose.see();
hose.mai();
}
}


### 2.6.7 CGLIB与JDK动态代理区别

1. **jdk动态代理是由[Java](http://lib.csdn.net/base/java)内部的反射机制来实现的,cglib动态代理底层则是借助asm来实现的。**
2. **反射机制在生成类的过程中比较高效,而asm在生成类之后的相关执行过程中比较高效(可以通过将asm生成的类进行缓存,这样解决asm生成类过程低效问题)。**
3. **jdk动态代理的应用前提,必须是目标类基于统一的接口。如果没有上述前提,jdk动态代理不能应用。**
4. **asm其实就是java字节码控制.**

注: **ASM是一个JAVA字节码分析、创建和修改的开源应用框架。在ASM中提供了诸多的API用于对类的内容进行字节码操作的方法。**
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值