下载地址:https://github.com/2020GetGoodOffer/test


Spring入门

Spring IoC

IoC即控制翻转,传统开发中当需要调用对象时需要调用者手动new操作,Spring中创建对象的工作交给了IoC容器来完成,实现了流程反转,DI则是IoC的一种具体实现方式。

  • 创建一个gradle项目,gradle的build.gradle配置文件内容如下:

    plugins {id 'java'id 'war'
    }group 'com.sjh'
    version '1.0-SNAPSHOT'sourceCompatibility = 1.8repositories {mavenLocal()mavenCentral()
    }dependencies {testCompile group: 'junit', name: 'junit', version: '4.12'compile group: 'org.springframework', name: 'spring-context', version: '5.0.2.RELEASE'providedCompile group: 'org.projectlombok', name: 'lombok', version: '1.16.12'
    }
    
  • 在src/main/java下创建一个实体类,并使用@Data注解(需要上面的lombok依赖)自动生成getter/setter方法:

    @Data
    public class Student {private Integer id;private String name;private Integer age;
    }
    
  • 在src/test/java下创建一个IoC的测试类,传统方式中,当我们创建一个Student对象会使用如下方式:

    public class IoCTest {@Testpublic void testIoC(){Student student = new Student();student.setId(1);student.setName("sjh");student.setAge(24);System.out.println(student);}}
    

    运行结果如下:

  • 如果要使用Spring实现控制反转,需要先在src/main/resources下创建一个名为spring.xml的XML配置文件:

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"><!-- 将Student交给spring容器 --><bean id="student" class="com.sjh.entity.Student"><property name="id" value="1"/><property name="name" value="sjh"/><property name="age" value="24"/></bean></beans>
    
  • 利用IoC获取对象(通过bean的id):

    ClassPathXmlApplicationContext获取了配置文件,通过配置文件对象的getBean方法获取一个Student的实例,由于在配置文件中已经注入了属性,此时student是有相关属性的。

    @Test
    public void testIoC2(){ApplicationContext ac = new ClassPathXmlApplicationContext("spring.xml");Student student = (Student) ac.getBean("student");System.out.println(student);
    }
    

    运行结果如下:


配置文件

通过bean标签来完成对象的管理,bean标签的属性如下:

  • id :对象名。

  • class :对象的模板类,所有交给IoC容器管理的类必须有无参构造器,因为Spring底层通过反射调用无参构造器创建对象的。

  • 对象的成员变量通过property标签完成赋值

    • name :成员变量名。

    • value :成员变量的值(基本数据类型和String)。

    • ref :成员变量的值(引用数据类型),将IoC中的另外一个bean赋值给当前成员变量(DI)。

      例如新创建一个Address类作为地址:

      @Data
      public class Address {private String country;private String city;}
      

      给Student类添加Address地址属性:

      @Data
      public class Student {private Integer id;private String name;private Integer age;private Address address;
      }
      

      在spring.xml中使用ref注入Address属性:

      <!-- 将Student交给spring容器 -->
      <bean id="student" class="com.sjh.entity.Student"><property name="id" value="1"/><property name="name" value="sjh"/><property name="age" value="24"/><property name="address" ref="address"/>
      </bean><bean id="address" class="com.sjh.entity.Address"><property name="country" value="China"/><property name="city" value="xi'an"/>
      </bean>
      

      此时运行的结果:


IoC底层原理

  • 读取配置文件,解析XML。
  • 通过反射机制实例化配置文件中所配置的所有bean。

实例演示,首先在build.gradle中引入dom4j用来解析XML文件:

dependencies {testCompile group: 'junit', name: 'junit', version: '4.12'compile group: 'org.springframework', name: 'spring-context', version: '5.0.2.RELEASE'providedCompile group: 'org.projectlombok', name: 'lombok', version: '1.16.12'compile group: 'dom4j', name: 'dom4j', version: '1.6.1'
}

自定义ApplicationContext:

public interface MyApplicationContext {Object getBean(String id);
}

自定义ClassPathXmlApplicationContext:

public class MyClassPathXmlApplicationContext implements MyApplicationContext {private Map<String,Object> iocMap = new HashMap<>();public MyClassPathXmlApplicationContext(String path) throws Exception {SAXReader reader = new SAXReader();//获取XML配置文件对象Document document = reader.read("./src/main/resources/"+path);//获取根节点,即beans标签Element root = document.getRootElement();//获取迭代器Iterator<Element> iterator = root.elementIterator();while(iterator.hasNext()){Element element = iterator.next();//获取bean标签的id内容String id = element.attributeValue("id");//获取bean标签的class内容String className = element.attributeValue("class");//通过反射创建对象Class clazz = Class.forName(className);//通过无参构造器创建对象Object instance = clazz.getConstructor().newInstance();//给对象的属性赋值Iterator<Element> proIter = element.elementIterator();while (proIter.hasNext()){Element property = proIter.next();//获取property标签对象String name = property.attributeValue("name");//获取property标签的name,即成员变量名String valueStr = property.attributeValue("value");//获取property标签的value,即成员变量值String methodName = "set"+name.substring(0,1).toUpperCase()+name.substring(1);//属性首字母大写Field field = clazz.getDeclaredField(name);Method method = clazz.getDeclaredMethod(methodName,field.getType());//第二个参数是set方法的参数类型Object value;if(field.getType().getName().equals("java.lang.Integer")) {//根据成员变量数据类型转换valuevalue = Integer.parseInt(valueStr);}else {value = valueStr;}method.invoke(instance,value);//通过反射赋值}//把对象存入mapiocMap.put(id,instance);}Object address = iocMap.get("address");Object student = iocMap.get("student");Class<?> clazz = student.getClass();Method setAddress = clazz.getDeclaredMethod("setAddress", Address.class);setAddress.invoke(student,address);}@Overridepublic Object getBean(String id) {return iocMap.get(id);}
}

测试方法:

    @Testpublic void testIoC3() throws Exception {MyApplicationContext ac=new MyClassPathXmlApplicationContext("spring.xml");Student student = (Student) ac.getBean("student");System.out.println(student);}

至此成功模拟了IoC的实现原理,运行结果如下:


通过运行时类获取bean的问题

@Test
public void testIoC4(){ApplicationContext ac = new ClassPathXmlApplicationContext("spring.xml");Student student = ac.getBean(Student.class);System.out.println(student);
}

可以不使用bean的id而使用bean的.class形式获取实例,这种方法的问题是,配置文件中一个类只能有一个实例,也就是XML文件中只能配置一个Student的实例,不能配置多个。当使用以下配置时运行测试方法就会报错:

<bean id="student" class="com.sjh.entity.Student"><property name="id" value="1"/><property name="name" value="sjh"/><property name="age" value="24"/><property name="address" ref="address"/>
</bean><bean id="student2" class="com.sjh.entity.Student"><property name="id" value="2"/><property name="name" value="sjh"/><property name="age" value="24"/><property name="address" ref="address"/>
</bean>

通过有参构造器创建bean

在Student类上加上以下注解:

@NoArgsConstructor
@AllArgsConstructor

spring.xml中配置如下:

<!-- 有参构造器创建bean -->
<bean id="student2" class="com.sjh.entity.Student"><constructor-arg name="id" value="2"/><constructor-arg name="name" value="sjh"/><constructor-arg name="age" value="25"/><constructor-arg name="address" ref="address"/>
</bean>

测试方法:

@Test
public void testIoC5(){ApplicationContext ac = new ClassPathXmlApplicationContext("spring.xml");Student student = (Student) ac.getBean("student2");System.out.println(student);
}

运行结果:


给bean注入集合

假设此时一个学生不仅有一个地址,那么我们希望一个学生对象可以存储多个地址,首先修改Student类中address为List类型:

@Data
@NoArgsConstructor
@AllArgsConstructor
public class Student {private Integer id;private String name;private Integer age;private List<Address> addresses;
}

在spring.xml中注入集合:

<bean id="student" class="com.sjh.entity.Student"><property name="id" value="1"/><property name="name" value="sjh"/><property name="age" value="24"/><property name="addresses"><list><ref bean="address1"/><ref bean="address2"/></list></property>
</bean><bean id="address1" class="com.sjh.entity.Address"><property name="country" value="China"/><property name="city" value="xi'an"/>
</bean><bean id="address2" class="com.sjh.entity.Address"><property name="country" value="China"/><property name="city" value="shanghai"/>
</bean>

scope作用域

scope表示bean的作用域,一共有4种:

  • singleton:单例模式,表示通过Spring容器获取的bean是唯一的。无论是否获取bean,在加载配置文件时,Spring就会对bean进行实例化,加载模式属于立即加载。
  • prototype :原型模型,表示通过Spring容器获取的bean是不同的。在获取bean时Spring才会对bean进行实例化,加载模式属于延迟加载。
  • request:请求模式,表示在一次HTTP请求内有效。
  • session:会话模式,表示在一次HTTP会话中有效。

request和session只适用与Web项目,大多数情况下使用singleton和prototype较多。

默认情况下,bean是单例模式的,从容器中获取两个Student对象,比较是否相等:

@Test
public void testScope1(){ApplicationContext ac = new ClassPathXmlApplicationContext("spring.xml");Student student1 = (Student) ac.getBean("student");Student student2 = (Student) ac.getBean("student");System.out.println(student1);System.out.println(student2);System.out.println(student1==student2);
}

运行结果:

在spring.xml中修改scope为原型模式:

<bean id="student" class="com.sjh.entity.Student" scope="prototype">
//..省略

此时再次运行,此时从Spring容器获得的对象已经是不同的实例了:


Spring的继承

与Java的继承不同,Java是类层面的继承,子类可以继承父类的结构信息,Spring是对象层面的继承,子对象可以继承父对象的属性值。

只需要在bean标签中,加入一个parent属性即可:

<bean id="student" class="com.sjh.entity.Student" scope="prototype"><property name="id" value="1"/><property name="name" value="sjh"/><property name="age" value="24"/><property name="addresses"><list><ref bean="address1"/><ref bean="address2"/></list></property>
</bean><bean id="student2" class="com.sjh.entity.Student" parent="student"/>

测试方法:

@Test
public void testExtends(){ApplicationContext ac = new ClassPathXmlApplicationContext("spring.xml");Student student1 = (Student) ac.getBean("student");Student student2 = (Student) ac.getBean("student2");System.out.println(student1);System.out.println(student2);
}

可以看到,student2继承了student1的全部值:

值得一提的是,即使是不同的两个类也可以继承,但是继承的类中的属性要大于等于被继承的类。

例如,创建一个User类,不仅有Student类的四个属性,还有额外的工资属性:

@Data
@NoArgsConstructor
@AllArgsConstructor
public class User {private Integer id;private String name;private Integer age;private List<Address> addresses;private Double salary;
}

在spring.xml中实现继承:

<bean id="student" class="com.sjh.entity.Student" scope="prototype"><property name="id" value="1"/><property name="name" value="sjh"/><property name="age" value="24"/><property name="addresses"><list><ref bean="address1"/><ref bean="address2"/></list></property>
</bean><bean id="user" class="com.sjh.entity.User" parent="student"/>

测试方法:

@Test
public void testExtends2(){ApplicationContext ac = new ClassPathXmlApplicationContext("spring.xml");Student student = (Student) ac.getBean("student");User user = (User) ac.getBean("user");System.out.println(student);System.out.println(user);
}

运行结果如下,可以看到Student对象的全部值都继承了:


Spring的依赖

与继承类似,依赖也是描述bean之间的一种关系,配置依赖关系后,被依赖的bean一定会先被创建。

默认情况下,创建bean的顺序是按照XML中定义的顺序创建的:

<bean id="student2" class="com.sjh.entity.Student"/><bean id="user" class="com.sjh.entity.User"/>

在Student和User类中创建无参构造器,输出一句话帮助测试:

@Data
@AllArgsConstructor
public class Student {public Student(){System.out.println("创建了Student对象");}...
}    @Data
@AllArgsConstructor
public class User {public User(){System.out.println("创建了User对象");}...
}

测试方法:

@Test
public void testDep1(){ApplicationContext ac = new ClassPathXmlApplicationContext("spring.xml");ac.getBean("student2");ac.getBean("user");
}

此时bean的创建顺序:

为了证明创建bean的顺序和XML文件定义顺序有关,调换其定义顺序:

<bean id="user" class="com.sjh.entity.User"/><bean id="student2" class="com.sjh.entity.Student"/>

此时bean的创建顺序:

为了证明被依赖的对象先被创建,我们可以让User定义在Student前面,让User依赖Student:

<bean id="user" class="com.sjh.entity.User" depends-on="student2"/><bean id="student2" class="com.sjh.entity.Student"/>

此时bean的创建顺序:


Spring的p命名空间

p命名空间是对IoC/DI的简化,使用p命名空间可以更加方便地完成bean的配置以及bean之间的依赖注入。

在beans标签中引入p命名空间:

xmlns:p="http://www.springframework.org/schema/p"

配置bean:

<!-- 使用p命名空间构建依赖关系 -->
<bean id="stu" class="com.sjh.entity.Student" p:id="1" p:name="sjh" p:age="24" p:addresses-ref="addr"/><bean id="addr" class="com.sjh.entity.Address" p:country="China" p:city="beijing"/>

Spring的工厂方法

IoC通过工厂模式创建bean的方式有两种:

  • 静态工厂方法

    创建一个实体类Car

    @Data
    @AllArgsConstructor
    public class Car {private Integer id;private String name;}
    

    创建一个静态工厂类

    //创建Car的静态工厂
    public class StaticFactory {private static Map<Integer, Car> carMap;static {carMap = new HashMap<>();carMap.put(1,new Car(1,"宝马"));carMap.put(2,new Car(2,"奔驰"));}public static Car getCar(Integer id){return carMap.get(id);}
    }
    

    在XML文件中进行配置

    <!-- 通过静态工厂创建bean-->
    <bean id="car" class="com.sjh.factory.StaticFactory" factory-method="getCar"><constructor-arg value="1"/>
    </bean>
    

    测试方法:

    @Test
    public void testFactory1(){ApplicationContext ac = new ClassPathXmlApplicationContext("factory.xml");Car car = (Car) ac.getBean("car");System.out.println(car);
    }
    

    运行结果:

  • 实例工厂方法

    创建一个实例工厂

    //创建Car的实例工厂
    public class InstanceFactory {private Map<Integer, Car> carMap;public InstanceFactory(){carMap = new HashMap<>();carMap.put(1,new Car(1,"宝马"));carMap.put(2,new Car(2,"奔驰"));}public Car getCar(Integer id){return carMap.get(id);}
    }
    

    在XML中配置

    <!-- 通过实例工厂创建bean-->
    <bean id="car2" factory-bean="instanceFactory" factory-method="getCar"><constructor-arg value="2"/>
    </bean><bean id="instanceFactory" class="com.sjh.factory.InstanceFactory"/>
    

    测试方法:

    @Test
    public void testFactory2(){ApplicationContext ac = new ClassPathXmlApplicationContext("factory.xml");Car car = (Car) ac.getBean("car2");System.out.println(car);
    }
    

    运行结果:


IoC自动装载(Autowire)

IoC负责创建对象,DI负责完成对象的依赖注入,通过配置property标签的ref属性完成,除此之外还提供了一种更简便的依赖注入方式:自动装载,不需要手动配置property,IoC容器会自动选择bean完成注入。

自动装载有两种方式:

  • byName:通过属性名自动装载

    创建一个Person类

    @Data
    public class Person {private Integer id;private String name;private Car car;
    }
    

    在XML中通过自动装载为其注入car属性

    <bean id="car" class="com.sjh.entity.Car"><property name="id" value="1"/><property name="name" value="特斯拉"/>
    </bean><bean id="person" class="com.sjh.entity.Person" autowire="byName"><property name="id" value="1"/><property name="name" value="sjh"/>
    </bean>
    

    测试方法:

    @Test
    public void testAutowired1(){ApplicationContext ac = new ClassPathXmlApplicationContext("autowire.xml");Person person = (Person) ac.getBean("person");System.out.println(person);
    }
    

    运行结果:

  • byType:通过属性的数据类型自动装载

    当把car这个bean的id改为dog时,byName就会失败:

    <bean id="dog" class="com.sjh.entity.Car"><property name="id" value="1"/><property name="name" value="特斯拉"/>
    </bean>
    

    运行结果:

    可以通过属性的类型装载:

    <bean id="person" class="com.sjh.entity.Person" autowire="byType"><property name="id" value="1"/><property name="name" value="sjh"/>
    </bean>
    

    运行结果:

    但是当出现多个类型相同的bean时,就不能使用byType了。


Spring AOP

AOP即Aspect Oriented Programing,面向切面编程。AOP是面向对象编程的一个补充,在运行时动态地将代码切入到类的指定方法、指定位置上的编程思想就是面向切面编程。将不同方法的同一个位置抽象成一个切面对象,对该切面进行编程。

优点:

  • 降低模块之间的耦合度,使系统更容易扩展,可以更好实现代码复用
  • 将复杂需求分层,非业务代码更加集中,便于管理,业务代码更加简单纯粹

使用动态代理实现AOP

  • 在build.gradle的依赖中加入AOP的相关依赖:

    compile group: 'org.springframework', name: 'spring-aop', version: '5.0.2.RELEASE'
    compile group: 'org.springframework', name: 'spring-aspects', version: '5.0.2.RELEASE'
    
  • 创建一个计算器接口,定义加减乘除四个方法:

    public interface Cal {//加法int add(int a, int b);//减法int sub(int a, int b);//乘法int mul(int a, int b);//除法int div(int a, int b);}
    
  • 创建计算器接口的实现类:

    public class CalImpl implements Cal {@Overridepublic int add(int a, int b) {System.out.println("add方法的参数是:"+a+","+b);int res = a + b;System.out.println("add方法的结果是:"+res);return res;}@Overridepublic int sub(int a, int b) {System.out.println("sub方法的参数是:"+a+","+b);int res = a - b;System.out.println("sub方法的结果是:"+res);return res;}@Overridepublic int mul(int a, int b) {System.out.println("mul方法的参数是:"+a+","+b);int res = a * b;System.out.println("mul方法的结果是:"+res);return res;}@Overridepublic int div(int a, int b) {System.out.println("div方法的参数是:"+a+","+b);int res = a / b;System.out.println("div方法的结果是:"+res);return res;}
    }
    
  • 创建一个测试类和测试方法:

    public class AOPTest {@Testpublic void testCal1(){Cal cal = new CalImpl();cal.add(1,1);cal.sub(2,2);cal.mul(3,3);cal.div(4,4);}
    }
    

    运行结果:

    可以发现实现类中除了计算加减乘除的具体逻辑不同之外,前后的输出代码都是大量重复的,日志信息和业务逻辑的耦合度很高,不利于系统维护,我们希望可以把它提取出来统一管理。

    可以使用AOP进行优化,具体可以使用动态代理的方式来实现,这样业务代码只需要关注具体业务即可。

  • 创建一个可以获取代理对象的MyInvocationHandler类:

    public class MyInvocationHandler implements InvocationHandler {//委托对象,即计算器实现类private Object object = null;//返回代理对象public Object getProxy(Object object){this.object = object;//动态代理的第一个参数 委托类的类加载器,第二个参数 委托类的接口,代理类必须实现其接口,第三个参数 具体的处理逻辑return Proxy.newProxyInstance(object.getClass().getClassLoader(),object.getClass().getInterfaces(), this);}@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {String methodName = method.getName();System.out.println(methodName+"方法的参数是:"+ Arrays.toString(args));Object res = method.invoke(this.object, args);System.out.println(methodName+"方法的结果是:"+ res);return res;}}
    
  • 此时可以优化原来的计算器实现类,省去冗余代码,只关心具体逻辑:

    public class CalImpl implements Cal {@Overridepublic int add(int a, int b) {int res = a + b;return res;}@Overridepublic int sub(int a, int b) {int res = a - b;return res;}@Overridepublic int mul(int a, int b) {int res = a * b;return res;}@Overridepublic int div(int a, int b) {int res = a / b;return res;}
    }
    
  • 测试方法:

    @Test
    public void testCal2(){Cal cal = new CalImpl();Cal proxy = (Cal) new MyInvocationHandler().getProxy(cal);proxy.add(1,1);proxy.sub(2,2);proxy.mul(3,3);proxy.div(4,4);
    }
    

    运行结果是相同的:


使用Spring AOP

通过Spring的AOP实现上述功能,Spring中不需要创建InvoationHandler,只需要创建一个切面对象,将所有的非业务代码在切面对象中完成即可,Spring底层会自动根据切面类以及目标类来生成代理对象。


创建一个切面类:

@Aspect//标识该类是一个切面类
@Component//将该类的对象交给IoC容器管理
public class LoggerAspect {@Before("execution(public int com.sjh.utils.impl.CalImpl.*(..))")public void before(JoinPoint joinPoint){//获取方法名String methodName = joinPoint.getSignature().getName();//获取参数String args = Arrays.toString(joinPoint.getArgs());System.out.println(methodName+"方法的参数是:"+ args);}@AfterReturning(value = "execution(public int com.sjh.utils.impl.CalImpl.*(..))",returning = "result")public void afterReturning(JoinPoint joinPoint,Object result){//获取方法名String methodName = joinPoint.getSignature().getName();System.out.println(methodName+"方法的结果是:"+result);}@After("execution(public int com.sjh.utils.impl.CalImpl.*(..))")public void after(JoinPoint joinPoint){//获取方法名String methodName = joinPoint.getSignature().getName();System.out.println(methodName+"方法执行完毕");}}

通知的相关注解说明:

@Before:前置通知,指在某个连接点之前执行的通知。

@After:后置通知,指某个连接点退出时执行的通知(不论正常返回还是异常退出)。

@AfterReturning:返回后通知,指某连接点正常完成之后执行的通知,返回值使用returning属性接收。

@AfterThrowing:异常通知,指方法抛出异常导致退出时执行的通知,和@AfterReturning只会有一个执行,异常使用throwing属性接收。


在aop.xml中进行配置:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:aop="http://www.springframework.org/schema/aop"xmlns:context="http://www.springframework.org/schema/context"xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsdhttp://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"><!-- 自动扫描类,寻找需要被IoC管理的类 --><context:component-scan base-package="com.sjh"/><!-- 使Aspect注解生效,为目标类自动生成代理对象 --><aop:aspectj-autoproxy/></beans>

测试方法:

@Test
public void testCal3(){ApplicationContext ac = new ClassPathXmlApplicationContext("aop.xml");Cal proxy = (Cal) ac.getBean("calImpl");//name默认使用被代理类的类名首字母小写形式proxy.add(1,1);proxy.sub(2,2);proxy.mul(3,3);proxy.div(4,4);
}

结果:

需要注意的是@AfterReturning会在@After之后执行。


环绕通知

除了使用前置通知、后置通知、返回后通知配置通知顺序外,还可以使用环绕通知直接配置,环绕通知是指包围一个连接点的通知:

@Around("pc()")
public Object after(ProceedingJoinPoint pjp){Object res;String methodName = null;try{//获取参数Object[] args = pjp.getArgs();methodName = pjp.getSignature().getName();System.out.println(methodName + "方法的参数是:" + Arrays.toString(args));//获取方法返回结果res = pjp.proceed(args);System.out.println(methodName + "方法的执行结果是:" + res);return res;}catch (Throwable t){throw new RuntimeException(t);}finally {System.out.println(methodName + "方法执行完毕");System.out.println("------------------------");}
}

测试结果:


切入点表达式说明

可以使用@Pointcut提取切入点表达式的配置:

@Pointcut("execution(*  *..CalImpl.*(int,int))")
private void pc(){}@Before("pc()")
public void before(JoinPoint joinPoint){...
}@AfterReturning(value = "pc()",returning = "result")
public void afterReturning(JoinPoint joinPoint,Object result){...
}@After("pc()")
public void after(JoinPoint joinPoint){...
}

表达式语法:

execution([修饰符] 返回值类型 包名.类名.方法名(参数))

全匹配方式:

public int com.sjh.utils.impl.CalImpl.add(int,int)

访问修饰符可以省略:

int com.sjh.utils.impl.CalImpl.add(int,int)

返回值可以使用*号,表示任意返回值

*  com.sjh.utils.impl.CalImpl.add(int,int)

包名可以使用*号,表示任意包,但是有几级包,需要写几个

*  *.*.*.*.CalImpl.add(int,int)

使用..来表示当前包,及其子包

*  *..CalImpl.add(int,int)

类名可以使用*号,表示任意类

*  *..*.add(int,int)

方法名可以使用 *号,表示任意方法

*  *..*.*(int,int)

参数列表可以使用*,表示参数可以是任意数据类型,但是必须有参数

*  *..*.*(*)

参数列表可以使用..表示有无参数均可,有参数可以是任意类型

*  *..*.*(..)

AOP相关概念

  • **JointPoint 连接点:**程序执行过程中的某一行为,即业务层中的所有方法。

  • Pointcut 切点:匹配的连接点,即要增强的那些连接点。切点一定是连接点,但连接点不一定是切点。

  • **Advice 通知:**指切面对于某个连接点所产生的动作,一个切面可以包含多个通知,包括前置通知、后置通知、返回后通知、异常通知、环绕通知。

  • **Aspect 切面:**一个关注点的模块化,这个关注点可能会横切多个对象。

  • Target 目标:一个或多个切面所通知的对象。

  • **Proxy 代理:**AOP使用两种代理,JDK动态代理和CGLib代理。

  • **Weaving 织入:**是指把增强应用到目标对象来创建新的代理对象的过程。


Spring快速入门实战笔记相关推荐

  1. Docker快速入门总结笔记

    文章目录 1. Docker概述 2. 虚拟化技术和容器化技术 3. Docker的基本组成 4. Docker的安装 5. Docker的卸载 6. 配置阿里云镜像加速 8. Docker容器运行流 ...

  2. Python3网络爬虫快速入门实战解析(一小时入门 Python 3 网络爬虫)

    Python3网络爬虫快速入门实战解析(一小时入门 Python 3 网络爬虫) https://blog.csdn.net/u012662731/article/details/78537432 出 ...

  3. python3 爬虫实例_【实战练习】Python3网络爬虫快速入门实战解析(上)

    原标题:[实战练习]Python3网络爬虫快速入门实战解析(上) 摘要 使用python3学习网络爬虫,快速入门静态网站爬取和动态网站爬取 [ 前言 ] 强烈建议:请在电脑的陪同下,阅读本文.本文以实 ...

  4. Java快速入门学习笔记9 | Java语言中的方法

    有人相爱,有人夜里开车看海,有人却连LeetCode第一题都解不出来!虽然之前系统地学习过java课程,但是到现在一年多没有碰过Java的代码,遇到LeetCode不知是喜是悲,思来想去,然后清空自己 ...

  5. Java快速入门学习笔记8 | Java语言中的数组

    有人相爱,有人夜里开车看海,有人却连LeetCode第一题都解不出来!虽然之前系统地学习过java课程,但是到现在一年多没有碰过Java的代码,遇到LeetCode不知是喜是悲,思来想去,然后清空自己 ...

  6. Java快速入门学习笔记7 | Java语言中的类与对象

    有人相爱,有人夜里开车看海,有人却连LeetCode第一题都解不出来!虽然之前系统地学习过java课程,但是到现在一年多没有碰过Java的代码,遇到LeetCode不知是喜是悲,思来想去,然后清空自己 ...

  7. Java快速入门学习笔记6 | Java语言中的for循环语句

    有人相爱,有人夜里开车看海,有人却连LeetCode第一题都解不出来!虽然之前系统地学习过java课程,但是到现在一年多没有碰过Java的代码,遇到LeetCode不知是喜是悲,思来想去,然后清空自己 ...

  8. Java快速入门学习笔记5 | Java语言中的while循环语句

    有人相爱,有人夜里开车看海,有人却连LeetCode第一题都解不出来!虽然之前系统地学习过java课程,但是到现在一年多没有碰过Java的代码,遇到LeetCode不知是喜是悲,思来想去,然后清空自己 ...

  9. Java快速入门学习笔记4 | Java语言中的if条件语句

    有人相爱,有人夜里开车看海,有人却连LeetCode第一题都解不出来!虽然之前系统地学习过java课程,但是到现在一年多没有碰过Java的代码,遇到LeetCode不知是喜是悲,思来想去,然后清空自己 ...

最新文章

  1. SQL Server 2005下的分页SQL
  2. 软件工程第二次课后作业——Gaoooo
  3. Serializer序列化使用
  4. 浅谈RNN,LSTM和GRU
  5. 使用mount挂载一个windows的共享
  6. PHP5.5中新增的参数跳跃和生成器功能介绍
  7. 一个不知名的网站复制来的: java怎样连接到SQL server 2008
  8. Go泛型草案设计简明指南
  9. SpringBoot2.0.3 + SpringSecurity5.0.6 + vue 前后端分离认证授权
  10. 如何打开电脑上的安全策略
  11. spring security原理图及其解释
  12. 【TSP】基于matlab模拟退火算法求解34城市旅行商问题【含Matlab源码 882期】
  13. python面板数据模型操作步骤_面板数据模型估计一般要做哪些步骤
  14. 如何在Google地图上找到经度和纬度
  15. 网页压缩--gzip和deflate的区别
  16. excel中插入的图表保存时提示 无法保存 html,Excel技巧:将图表另存为GIF文件
  17. 什么是node.js
  18. Unity性能优化(2)-官方教程Diagnosing performance problems using the Profiler window翻译
  19. 有趣的数字环的算法实现
  20. Django——Ajax

热门文章

  1. mysql的安全权限_MySQL 权限与安全
  2. 全面屏虚拟按键高度适配
  3. vue高德地图显示世界地点信息分布的多窗口问题
  4. python 的 pip
  5. 标准结构篇:5)热(散热)设计
  6. 项目实战:p2p文件下载器
  7. 第七课 程小奔之奔跑的兔子
  8. 阿里开源规则引擎QLExpress-入门实战
  9. 偷偷告诉你,十大有效的推广引流渠道有哪些?
  10. Android telephony相关