一、自定义注解
- 什么是注解?
注解是JDK1.5新增的技术(主要有:注解、泛型、并发编程用的并发包),作用是提高编程效率。很多框架为了简化代码,都会提供有些注解,如springboot的微服务架构就摒弃了xml配置使用注解实现配置。注解可以理解为插件,是代码级别的插件,在类的方法上写:@XXX,就是在代码上插入了一个插件。注解不会也不能影响代码的实际逻辑,仅仅起到辅助性的作用。
为什么要理解注解实现原理?因为在企业级开发过程中,架构师角色一般会针对代码进行重构(运用设计模式、自定义注解等),让别人使用注解,更方便高效的进行开发工作。 - 注解的分类
- 内置注解(jdk自带的注解,也称为元注解)
如:@SuppressWarnings,对有警告的代码前面加上,可以在javac编译中去除警告
@Deprecated,表示带有标记的包,方法,字段已过时
@Overricle,加上这个标记,说明该方法是将父类的方法重写 - 自定义注解(Spring框架的注解)
元注解的作用就是负责注解其他注解。Java5.0定义了4个标准的meta-annotation类型,它们被用来提供对其它 annotation类型作说明。Java5.0定义的元注解:- @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声明 - @Retention
表示需要在什么级别保存该注释信息,用于描述注解的生命周期(即:被描述的注解在什么范围内有效)
RetentionPolicy的取值包含以下三种:
SOURCE:源码级别保留,编译后即丢弃。
CLASS:编译级别保留,编译后的class文件中存在,在jvm运行时丢弃,这是默认值。
RUNTIME: 运行级别保留,编译后的class文件中存在,在jvm运行时保留,可以被反射调用。 - @Documented
表明这个注解应该被 javadoc工具记录.,默认情况下javadoc是不包括注解的. 但如果声明注解时指定了 @Documented,则它会被 javadoc 之类的工具处理, 所有注解类型信息也会被包括在生成的文档中,是一个标记注解,没有成员。 - @Inherited
@Inherited是一个标记注解,阐述了某个被标注的类型是被继承的。 如果一个使用了@Inherited修饰的annotation类型被用于一个class,则这个annotation将被用于该class的子类。
- @Target
- 内置注解(jdk自带的注解,也称为元注解)
- ORM映射框架实现原理:
数据库表采用驼峰式命名字段,如:user_name、user_age,而Java实体类中对应的属性名为:userName、userAge,例如在Hibernate框架中对属性加上@Column(“user_name”) 就能实现实体和数据库关系的映射,这个ORM框架实体类与表字段不一致,底层生成sql语句原理如下:- 创建两个自定义注解,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(); }
- 创建实体类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; } }
- 创建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()); } }
- 创建两个自定义注解,Table.java、Column.java。
二、常用设计模式
- 什么是设计模式?
设计模式(Design pattern)是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结。使用设计模式是为了可重用代码、让代码更容易被他人理解、保证代码可靠性。 毫无疑问,设计模式于己于他人于系统都是多赢的,设计模式使代码编制真正工程化,设计模式是软件工程的基石,如同大厦的一块块砖石一样。项目中合理的运用设计模式可以完美的解决很多问题,每种模式在现在中都有相应的原理来与之对应,每一个模式描述了一个在我们周围不断重复发生的问题,以及该问题的核心解决方案,这也是它能被广泛应用的原因。
通俗来说,设计模式就是前人针对特定问题的一套解决方案,或者说是套路。 - 为什么要使用设计模式?
代理设计模式:打印请求、响应的日志
工厂设计模式:负责初始化一些Bean
模板方法: 重构
观察者设计模式: 负责事件监听
最终目的就是,提高代码的复用性(简化代码,跟自定义注解功能相似,都是简化代码,但是思想不一样,设计模式是面向对象的思想,接口、父类、抽象类都是为了设计模式)、健壮性,让别人能够易懂。 - 设计模式的分类:
总体来说设计模式分为三大类,23种设计模式:
创建型模式(主要对bean做一些操作,初始化之类的),共五种:工厂方法模式、抽象工厂模式、单例模式、建造者模式、原型模式。
结构型模式(对象拥有什么样的结构进行管理),共七种:适配器模式、装饰器模式、代理模式、外观模式、桥接模式、组合模式、享元模式。
行为型模式(对对象和方法做一些处理),共十一种:策略模式、模板方法模式、观察者模式、迭代子模式、责任链模式、命令模式、备忘录模式、状态模式、访问者模式、中介者模式、解释器模式。
其实还有两类:并发型模式和线程池模式。 - 设计模式六大原则:
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)
原则是尽量使用合成/聚合的方式,而不是使用继承。 - 单例模式:
- 什么是单例?
单例保证一个对象JVM(在一个jvm而不是多个jvm)中只能有一个实例,常见单例 懒汉式、饿汉式
什么是懒汉式,就是需要的才会去实例化,线程不安全。
什么是饿汉式,就是当class文件被加载的时候,被初始化,天生线程安全。
相比来说懒汉式节约内存,但是懒汉式线程不安全,代码执行效率比饿汉式低。
为什么懒汉式比饿汉式代码执行效率低?
因为懒汉式使用了synchronized加上了同步锁,所以饿汉式代码执行效率高。 - 单例的代码实现:
- 懒汉式
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); } }
- 饿汉式
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.工厂模式:
- 什么是工厂模式?
工厂设计模式就是实现创建者和调用者分离。 - 工厂模式的代码实现:
- 简单工厂
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(); } }
- 工厂方法
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.代理模式
-
什么是代理?
通过代理控制对象的访问,可以详细访问某个对象的方法,在这个方法调用前处理,或调用后处理。既(AOP微实现) ,AOP核心技术面向切面编程。
代理设计模式就是控制一个对象的访问,优点:保证安全性。
SpringAOP里有方法的前置通知和后置通知,可以使用aop实现打印日志、权限判断、控制层打印请求响应。 -
代理应用场景:
安全代理:可以屏蔽真实角色
远程代理:远程调用代理类RMI(用的不多,只支持java语言,所以大多数都是http、webservice)
延迟加载:先加载轻量级代理类,真正需要在加载真实 -
代理的分类:
-
静态代理(静态定义代理类):
代码实现:- 创建接口House.java
package chauncy.designpattern.proxy.staticproxy; public interface House { public void sell(); }
- 创建接口实现类XiaoMing.java
package chauncy.designpattern.proxy.staticproxy; public class XiaoMing implements House{ @Override public void sell() { System.out.println("我要卖房。。。。"); } }
- 创建静态代理类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(); } }
- 创建接口House.java
-
动态代理(动态生成代理类):
代码实现:- 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(); } }
- 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(); } }
- jdk动态代理:
-
-
静态代理和动态代理有什么区别?
静态代理需要自己去实现代理类,一个接口一个代理类,很复杂,动态代理包含很多框架(jdk动态代理、cglib等),不要自己去实现代理类。 -
jdk动态代理与cglib动态代理的区别?
jdk动态代理是由Java内部的反射机制来实现的,cglib动态代理底层则是借助asm框架来实现的。总的来说,反射机制在生成类的过程中比较高效,而asm在生成类之后的相关执行过程中比较高效(可以通过将asm生成的类进行缓存,这样解决asm生成类过程低效问题)。还有一点必须注意:jdk动态代理的应用前提,必须是目标类基于统一的接口。如果没有上述前提,jdk动态代理不能应用。
注:asm其实就是java字节码控制。
SpringAOP底层用的是cglib动态代理,生成代理类的机制不是反射是使用asm来实现的。