• Spring带给了我们什么便利?
  • 注解版本的IOC如何玩?
    • 组件注册

      • 组件注册的过程中有哪些过滤规则?
      • 如何控制组件的作用域(单例多例)?
      • 六种注册组件的方式?
    • 生命周期
      • 什么是bean的生命周期
      • 在bean的生命周期的各个阶段我们可以插手做什么?
    • 属性赋值
      • 我们有哪些手段给bean的属性赋值?
    • 自动装配
      • 什么是自动装配?
      • Spring提供哪些注解可以帮我们实现自动装配?
      • java规范提供了哪些注解帮助我们实现了自动装配?
      • Spring提供的@Profile实现适配多环境?
  • 注解版本的AOP如何玩?

Spring带给了我们什么?

假如我们是第一次学习Spirng,我们大可不必关心spring带给我了我们什么便利,因为spring大概是web阶段筑基需要学习的第一个主流框架,初学难免会遇见各种各样的错误,所以尽管放心大胆的去学习如何使用就行了.先会用,其它的不用多想

过几天,用着熟悉了如何使用,再考虑spring究竟带给了我们什么便利也不迟, 那么,spring究竟带给了我们什么便利呢?

  • IOC(Inverse of Control),把对象的创建权反转给Spring容器,实现了解耦

我们使用Spring提供给我们的注解,把bean注册进IOC容器,进而把bean之间的依赖关系全部交给Spring管理,现在想想这绝对是一件超级赞的事情,可能原来的我会想,我自己可以new对象,干嘛非让Spring插一脚,是,假如我的项目就是个小demo全文一共三对象,完全顾的上来,那么spring对我来说就是累赘,但是!慢慢的接触的工程大了就会发现,离开了Spring,自己去管理那些对象之间的依赖会累死人的,而且SpringIOC的诞生也应正了bean的管理,完全不需要我们关系,我们关心的就是,我们如何向Spring索取依赖的Bean就行了,所以说Spring真的是web爱好者的小春天.

  • AOP(Aspect Oriented Programming),spring的aop

面向切面编程,aop说白了就是代码复用,把大量的重复代码从我们的业务逻辑中抽取出去,等程序运行的时候再动态的植入抽取出去的代码,这是件要多优雅就多优雅的事情!

应用:

  • 日志记录

  • 权限校验

  • 效率检查

  • 事务管理

  • 声明式事务

后端天天和数据库打交道,事务安全这样的不可能遇不见,我们这一代学习的人还真的是幸运,因为spring提供的声明式事务,我们不用再去苦苦的去写编程式事务,而且,现在spring4全面支持注解开发,我们甚至连配置文件都不用写,加一个注解就引入了spring的事务模块,激动不?

  • 方便集成其他开源框架

spring最擅长的事情就是整合这使它的生态变的超级庞大


例外给大家推荐一篇很棒的博客,里面详细图文论述了,spring究竟带给了我们什么?

spring实现解耦论述


注解版Spring-IOC怎么玩?

组件注册

只使用IOC,导入Spring-Context就可以

<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>4.3.7.RELEASE</version>
</dependency>

这个过程,我们要注意IOC以下几点

  • 组件注册的过程中有哪些过滤规则?
  • 如何控制组件的作用域(单例多例)?
  • 六种注册组件的方式?

组件注册及其过滤规则我串联在下面相邻的两部分里,这里先提一下Spring提供的所有过滤规则类型

在spring启动,开始扫描包,注册组件时,如果我们想按照我们的需求往IOC里面添加组件,就需要指定过滤规则,下面这五种类型的过滤规则,都源于我们在主配置类(相当于配置文件)上添加的@ComponentScan()包扫描注解

  • 按照注解过滤ANNOTATION
@ComponentScan(// 这个注解替换了原来配置文件中的包扫描
value="com.changwu.tryspring",
useDefaultFilters=false,
includeFilters = {
@Filter(type = FilterType.ANNOTATION, classes = Service.class)}
)
  • 按照给定的类型过滤
@Configuration
@ComponentScan(// 这个注解替换了原来配置文件中的包扫描
value="com.changwu.tryspring",
useDefaultFilters=false,
includeFilters = {
//只要是给定的类型的类,就可以加载进IOC,包括它的子类
@Filter(type = FilterType.ASSIGNABLE_TYPE,classes = BookDao.class)
}
)
  • 按照切面 (ASPECTJ) 基本不用

  • 按照正则 REGEX

  • 自定义规则 CUSTOM

@Configuration
@ComponentScan(// 这个注解替换了原来配置文件中的包扫描
value="com.changwu.tryspring",
useDefaultFilters=false,
includeFilters = {
// 只要是给定的类型的类,就可以加载进IOC,包括它的子类
@Filter(type = FilterType.CUSTOM,classes = MyTypeFilter.class)
}
)

注解版本中,配置类替换了原来的配置文件

@Configuration 注解标记本类为配置类

那么我不加@Configuration注解,这个类就不能成为配置类吗? 里面通过@Bean注解就添加不进bean来了? 不是的, @Configuration标记会被Spring使用Cglib技术拦截下来,换言之我们得到的bean,不再是我们自己new 的那种小白bean,而是被代理过的Bean

那么什么才是百分百的配置类呢? 配置类是我们调用如下代码所需要的那个类

  AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
applicationContext.register(配置类.class);

如下代码! 原来的包扫描被@ComponentScan 取代!!! 里面可以配置多个信息

  • 基础包信息value="com.XXX"
  • 排除被哪个注解标记的类excludeFilters = @Filter(type... classes...)
  • 只要被哪个注解标记的类,(配置文件版本的分两步!第一步禁用掉默认的过滤器),注解版同样分两步 useDefaultFilters=false, includeFilters = @Filter(classes= Service.class)

excludeFilters 把bean排除出IOC容器!!! 一般都是按照注解, 后面是 !!注解!!的Class数组,

另外,过滤器Filter的过滤类型,默认为按照注解过滤!!!

还可以在配置类中往IOC容器中组成对象!!!@Bean,用的时候 @Autowired

默认情况下,无论获取多少次,都是单实例的!!!

/**
* 配置类,等同于原来的配置文件
*
* @Author: Changwu
* @Date: 2019/3/31 16:57
*/
@Configuration
@ComponentScan(// 这个注解替换了原来配置文件中的包扫描
value="com.changwu.tryspring",
excludeFilters = @Filter( type =FilterType.ANNOTATION, classes = Repository.class)
)
// excludeFilters 把bean排除出IOC容器!!! 一般都是按照注解, 后面是 !!注解!!的Class数组, public class MainConfig {
/**
* 给容器注册bean
* 类型: 为返回值的类型
* id 默认使用方法名
* @return
*/
@Bean(value = "stu") // value可以自定义名字
public Student getStu(){
return new Student("张三",23);
}
}

此外在java8中,有如下代码,@ComponentSacn的倒数第二个注解是@Repeatble 意味着我们可以在主配置类上写多个@ComponentScan

 */
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Repeatable(ComponentScans.class)
public @interface ComponentScan {...}

自定义TypeFilter的过滤规则

自定义注解,要求实现FilterType接口,实现他的match(),该方法里面的两个参数都是接口类型的,我们可以从它里面取出关于IOC所扫描到的所有类的源信息,满足我们的预期,返回true,表示允许注册进IOC

public class MyTypeFilter implements TypeFilter {
/**
*
* @param metadataReader // 获取到当前的正在扫描的类的信息
* @param metadataReaderFactory // 可以获取到,其他任何类的信息
* @return
* @throws IOException
*/
@Override
public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {
AnnotationMetadata annotationMetadata = metadataReader.getAnnotationMetadata();//获取当前类的注解信息
ClassMetadata classMetadata = metadataReader.getClassMetadata();// 获取当前正在扫描类的 类信息, 比如实现了什么接口,有什么方法
Resource resource = metadataReader.getResource();// 获取类的资源信息, 比如在那个盘,路径 String className = classMetadata.getClassName();
System.out.println("---->"+className);
// 自定义,如果有@Repository注解,注入进去
Set<String> annotationTypes = annotationMetadata.getAnnotationTypes(); for (String annotationType : annotationTypes) {
System.out.println("++++++"+annotationType);
}
if (annotationMetadata.hasAnnotation("org.springframework.stereotype.Service")){
return true;
}
/* if (className.contains("er")){
return true;
}*/
return false;
}
}

@Scope, 设置组件的作用域

容器中的对象默认都是单实例的!!!,我们使用@Scope 注解改变这种状态

    /*
String SCOPE_SINGLETON = "singleton"; 单例
String SCOPE_PROTOTYPE = "prototype"; 多例
*/
@Scope(scopeName = "prototype")
@Bean(value = "stu2")
public Student getStu2(){
return new Student("李四",24);
} }

观察在单实例和多实例情况下,bean创建的时机

  • 默认在单实例的情况下,ioc容器创建完,直接加载bean

但是以后每次获取都会都是从IOC容器中获取

懒加载@Lazy! 针对单实例Bean,我们知道,容器启动的时候,就会创建一个唯一的bean,我们使用懒加载可以做到,在第一次使用的时候加载bean

  • 多实例情况下,只有在获取类时,IOC容器才会创建bean,!!!

以后每次获取Bean,ioc容器都会调用那个方法去new Bean


六种给容器注册组件的方法

着重看一种,按照条件注册bean @Conditional springboot底层大量的使用!!!

它的实现方式和自定义过滤条件相似

@Conditional : 按照条件注册bean

// 既可以标在类上,也可以标在方法上
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD})
public @interface Conditional { /**
* 所有的条件,都必须是注册过的!!!.
*/
Class<? extends Condition>[] value();
}

解释一下怎么用:

  • 它是个接口,需要的属性名为 value, 值是Class数组 ,可以进一步看到它的泛型是Condition接口 我们自己实现接口,重写里面的方法match(),根据方法提供的参数可以获取到我们想要的任何信息,返回true表示满足条件,注册bean,否则不注册

其他注册组件的方法

  • 方式1: 包扫描+注解 (@Cotroller @Service @Repository等等注解的类可以被IOC扫描到,添加进容器)

这种方法有局限性!!!, 只能是我们自己写的类, id为首字母小写的类名

  • 方式2: 在配置类内部 使用: @Bean注解,

更多的使用它注册第三方bean

  • 方式3: 在配置类头上使用  @Import(XXX.class,YYY.class)

解放了第二种通过@Bean还的自己new的缺陷!!! 组册进IOC中的组件的默认id,是组件的全类名

  • 方式4: @Import({Color.class, MyImportSelector.class})

实现ImportSelector接口: 返回需要导入的组件的全类名数组,完成批量导入

  • 方式5: 手动注册@Import({Color.class,MyImportSelector.class, MyImportBeanDefinitionRegisrar.class})

实现ImportBeanDefinitionRegistrar接口: 它里面的registerBeanDefinitions()方法的第二个参数就是BeanDefinitionRegistry, 所有bean注册进IOC容器都经由他完成,因此我们可以手动注册bean, 还可以通过第一个参数获取当前标注@Import注解的类的全部注解信息,加上第二个参数可以获取当前IOC容器的全部信息,动态判断是否要注入类到IOC

同时,第五种在SpringBoot中得到了大量的使用,实现SpringBoot的自动配置

  • 方式6 : 使用Spring提供的FactoryBean (工厂bean)
  1. 自己实现FactoryBean接口,重写三个方法
  2. @Bean,把自己实现的工厂bean添加到IOC
  3. 测试自己实现的BeanFactory的类型,是我们指定的泛型的类型的!!!
  4. 想获取到工厂的话, 需要添加前缀&
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MyConfig02.class);
Object getFB1 = applicationContext.getBean("&getFB");
Object getFB2 = applicationContext.getBean("getFB");

Bean的生命周期

通过第一阶段的学习,我们顺利把bean的创建权反转给了Spring,下面就来看看,IOC是如何控制Bean的生命周期的!

什么是bean的生命周期?

其实,就是下面的过程

Bean创建------> 初始化------> 销毁


在IOC管理bean生命周期的过程中,我们可以插手做什么?

  • 我们可以自定义bean的初始化和销毁方法,bean在到达相应的生命周期时,ioc会调用我们指定的方法,施加在bean上

原来的配置文件版本,需要我们写 init-method 和distroy-method


如何实现?

方法1: 使用@Bean注解完成

  @Bean(initMethod="init",destroyMethod = "destory")

测试类

 @Test
public void text6(){
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(LifeConfig.class);
Object car = applicationContext.getBean("car");
System.out.println(car);
applicationContext.close();
}

单实例情况下: 对象创建后并赋值好了后,执行init方法,容器关闭对象销毁

多实例情况下: 对象创建后并赋值好了后,执行init方法,IOC不会管理bean,需要我们自己去销毁bean

用处?

在配置数据源是大量使用,对象创建后需要初始话很多的数据,对象销毁了,很多资源要释放

方法2: 让Bean实现spring提供的两个接口InitializingBeanDisposableBean,重写这两个接口的方法

public interface InitializingBean {
// Bean初始化后调用
void afterPropertiesSet() throws Exception;
} public interface DisposableBean {
// Bean销毁后调用
void destroy() throws Exception;
}
  • 单例模式下: 在IOC一初始化就进行bean构造,并且执行完了的afterPropertiesSet()初始化,容器关闭,bean执行销毁DisposableBean()

方法3: 使用JS205规范提供的两个注解@PostConstruct和@PreDestory完成

  • @PostConstruct :在bean创建完成并且属性赋值好了后执行本初始化方法
  • @PreDestory: 在容器销毁bean之前,通知我们进行清理工作

这两个注解都是标注在方法上的!!!

@Documented
@Retention (RUNTIME)
@Target(METHOD)
public @interface PostConstruct {
} @Documented
@Retention (RUNTIME)
@Target(METHOD)
public @interface PreDestroy {
}

方法4: 使用Spring提供给我们的组件,接口BeanPostProcessor

这个组件很重要,Spring的底层,尤其是AOP底层大量使用它

里面有两个方法

public interface BeanPostProcessor {

   // 该方法,会在我们说的前三种初始化方法调用之前, 提前调用!!!
// 返回值,可以是我们新创建好的这个bean,也可以包装一下bean 再返回
/* Apply this BeanPostProcessor to the given new bean instance <i>before</i> any bean
* initialization callbacks (like InitializingBean's {@code afterPropertiesSet}
* or a custom init-method). The bean will already be populated with property values.
* The returned bean instance may be a wrapper around the original.
*/
Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException;
// 在上面我们所说的三种初始化方法调用之后,立刻调用!!!
Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException;
}

实现自己的BeanPostprocessor

@Component
public class MyBeanPostProcessor implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
System.out.println("postProcessBeforeInitialization执行了!!!"+" beanname=="+beanName+" bean=="+bean);
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
System.out.println("postProcessAfterInitialization执行了!!!"+" beanname=="+beanName+" bean=="+bean);
return bean;
}
}

测试结果如下图

划重点!!! --- BeanPostprocessor意味bean的后置处理器,前面也说了,它是spring提供给我们的一个辅助类型的组件,我们可以自定义BeanPostProcessor,在所有满足加入IOC容器的bean初始化方法前后执行BeanPostProcessor接口里的postProcessAfterInitialization()postProcessBeforeInitialization()方法,完成对bean的增强,AOP的底层使用的 后置处理器 是spring自动给我们加载进来的,他的这种特性,为AOP,动态植入代码的实现,提供的前提

接口BeanPostProcessor运行原理

  • 遍历得到容器中所有的BeanPostProcessor,挨个执行beforeInitialization,一旦返回值为null,说明ioc在没有这个对象,直接跳出for循环,不会执行BeanPostProcessor.postProcessBeforeInitialization()方法进行处理
populateBean(beanName,mdb,instanceWrapper);给bean的属性赋值

initlizeBean // 初始化
{
applyBeanPostProcessorBeforeInitialization() // 前置处理
invokeInitMethods(beanName,wrappedBean,mdb); // 执行初始化方法
applyBeanPostProcessorAfterInitialization() ; // 后置处理
}

通过这个继承图,可以看到,BeanPostProcessor作为后置处理器的顶级接口存在,程序运行打上断点,也能看到我们自定义的MyBeanPostProcessor,另外需要我们关心的一个实现是 InstantiationAwareBeanPosProcessor这个子接口,AOP的实现,就应用到了它(第二篇博客会记录)


使用配置文件给bean赋值

<bean id="person" class="com.changwu.bean" scope="prototype">
<property name="age" value="18"/>
<property name="name" value="zhangsan"/>
</bean>

使用注解的方法,给bean赋值

@Value

1. 基本数值
@Value("changwu")
private String name; 2. #{} SPEL表达式
@Value(value ="#{2*2}")
private int age; 3. ${} 取出配置文件中的值
@Value(value ="${student.age}")
private int age;

其中取出配置文件中的值,要在主配置类上添加@PropertySource(value = "classpath:/bean.properties")指明配置文件的路径

@Value+@PropertySource

前者使用${} 取出环境变量中的属性(程序运行后配置文件会加载进环境变量),后者给前者提供定位

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Repeatable(PropertySources.class) // 可以写多个
public @interface PropertySource { String name() default ""; //支持同时加载多个配置文件
String[] value(); // 忽略文件找不到
boolean ignoreResourceNotFound() default false; //设置编码
String encoding() default ""; /**
* Specify a custom {@link PropertySourceFactory}, if any.
* <p>By default, a default factory for standard resource files will be used.
* @since 4.3
* @see org.springframework.core.io.support.DefaultPropertySourceFactory
* @see org.springframework.core.io.support.ResourcePropertySource
*/
Class<? extends PropertySourceFactory> factory() default PropertySourceFactory.class;
}

自动装配?

  • 什么是自动装配?

自动装配就是,Spring利用依赖注入DI,完成对IOC容器中的各个组件依赖关系的赋值

@Autowired & @Qualifier & @Primary

@Autowired是Spring自己的注解

那些Bean,都在IOC中,通过@Autowired注解可以完成自动装配

  • 默认按照类型(XXX.class)去IOC中找到对应的组件

  • 如果存在多个bean,就按照id找( @Autowired标记的引用名)

  • 使用@Autowired,默认如果IOC中不存在该bean,就会报错

  • 通过设置@Autowired(required=false) 设置组件不必需存在于,IOC

  • @Autowired+@Qualifier 明确指定装配的bean的id

@Qualifier("bookDao2")
@Autowired
private BookDao bookDao;

再强调一遍,如果是包扫描的话,Bean在IOC的id是类名首字母小写,@Qualifier("XXX")不能乱写,要么是类名首字母小写,要么是我们通过@Bean("XXX")指定的id

  • @Primary 标记在我们手动添加进去的bean上,强制,首选注入!!!

但是,如果同时存在@Primary@Qualifier 依然会装配我们明确指定的Bean

// 构造器, 方法,参数,属性,全能!!!
@Target({ElementType.CONSTRUCTOR, ElementType.METHOD, ElementType.PARAMETER, ElementType.FIELD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Autowired { /**
* Declares whether the annotated dependency is required.
* <p>Defaults to {@code true}.
*/
boolean required() default true;
}
  1. @Autowired标注在方法上,方法的参数,默认从容器中获取
  2. @Autowired标注在构造器上,构造器需要的参数,默认从容器中获取
  3. @Bean标注的方法,方法的参数,默认从容器中获取,在参数位置的@Autowired可以省略不写

@Resources(JSR205) & @Inject(JSR330)

java规范注解

@Resources

  • 作用: 默认按照组件名称装配
  • 缺点: 不能和@Qulifier@Primary一起使用

@Inject

  • 还麻烦! 需要我们导入依赖
<dependency>
<groupId>javax.inject</groupId>
<artifactId>javax.inject</artifactId>
<version>1</version>
</dependency>

@Profile

Spring为我们提供的可以根据当前的环境,动态的激活和切换一系列组件的功能

比如在开发环境下,我们使用A数据库, 测试环境使用B数据库, 生产环境使用C数据库

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD})
@Documented
@Conditional(ProfileCondition.class)
public @interface Profile { /**
* The set of profiles for which the annotated component should be registered.
*/
String[] value(); }

支持写到方法上!满足@profile指定的环境下,方法的中组件的注入才会生效

支持写到类上!满足@profile指定的环境下,整个类的中组件的注入才会生效

  1. 准备工作,注册三个数据源
@Configuration
@PropertySource("classpath:/dbProperlies.properties")
public class MainConfiguration { @Value("${db.user}")
private String user; @Value("${db.driverClass}")
private String driverClass; @Profile("text")
@Bean("TextDBSource")
public DataSource dataSourceText(@Value("${db.password}") String pwd) {
ComboPooledDataSource dataSource = new ComboPooledDataSource();
dataSource.setUser(user);
dataSource.setPassword(pwd);
dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/text");
try {
dataSource.setDriverClass(driverClass);
} catch (PropertyVetoException e) {
e.printStackTrace();
}
return dataSource;
} @Profile("dev")
@Bean("DevDBSource")
public DataSource dataSourceDev(@Value("${db.password}") String pwd) {
ComboPooledDataSource dataSource = new ComboPooledDataSource();
dataSource.setUser(user);
dataSource.setPassword(pwd);
dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/person");
try {
dataSource.setDriverClass(driverClass);
} catch (PropertyVetoException e) {
e.printStackTrace();
}
return dataSource;
} @Profile("pro")
@Bean("ProductDBSource")
public DataSource dataSourceProduct(@Value("${db.password}") String pwd) {
ComboPooledDataSource dataSource = new ComboPooledDataSource();
dataSource.setUser(user);
dataSource.setPassword(pwd);
dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/ssh1");
try {
dataSource.setDriverClass(driverClass);
} catch (PropertyVetoException e) {
e.printStackTrace();
}
return dataSource;
}
}

2 . 结合@Profile注解,在组件上做一个标识,只有满足@Profile标识条件的才会被注册进IOC

  • 加了环境标识的bean,只有那个环境被激活,才会注册到容器中,默认是@Profile("default")
  • 没有环境标识的bean,任何条件下都会加载进容器

如何改变环境?

  1. 使用代码的方法
   @Test
public void text13(){
// 创建上下文
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
// 设置需要激活的环境
applicationContext.getEnvironment().setActiveProfiles("dev"); // 开发环境下注册组件
// 加载主配置类
applicationContext.register(MainConfiguration.class);
// 启动刷新容器
applicationContext.refresh();
String[] names = applicationContext.getBeanDefinitionNames();
for (String name : names) {
System.out.println(name);
}
applicationContext.close();
}

注解版AOP怎么玩?

AOP面向切面编程,指的是在程序运行期间,动态的将某段代码,切入到指定方法的指定位置额运行的编程方式 

导入AOP模块

<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>4.3.7.RELEASE</version>
</dependency>

创建业务逻辑类

  • 我们要做的就是在动态的在div运行前,运行后,出现异常时,正常结束,异常结束等不同情况下打印日志
public class MathAop {
public int div(int a,int b){
return a-b;
}
}

定义一个日志切面类

切面类上的方法会动态的感知到,切入点div的状态,在其指定的状态上,做出不同的反应而无序更改仍和div的代码

五种通知方法,对应五种不同的注解,具体的细节,在下面的代码中有注释

@Component
@Aspect
public class AopLog {

抽取切入表达式

  1. 注解@Pointcut
  2. 表达式可以使用* 通配
    • execution(pulbic com.changwu.tryspring.aop.MathAop.*(int,int))
    • execution(* com.changwu.tryspring.aop.MathAop.div(..))
  3. 本类使用 : 直接用方法名--> pointCut()
  4. 其他类使用: 使用带包名的全路径--> com.changwu.tryspring.aop.AopLog.pointCut()
    @Pointcut("execution(* com.changwu.tryspring.aop.MathAop.div(..))")
public void pointCut(){}

前置通知

  1. 注解@Before("切入点表达式")
  2. 调用时机: 在目标方法执行前执行
  3. 可选参数: JoinPoint,里面封装着切入点方法的全部信息,比如方法名,参数等等
    @Before("com.changwu.tryspring.aop.AopLog.pointCut()")
public void before(JoinPoint joinPoint){
Object[] args = joinPoint.getArgs(); // 获取参数
String name = joinPoint.getSignature().getName(); // 获取方法名
System.out.println("前置通知....参数:"+ Arrays.asList(args)+" 方法名: "+name);
}

后置通知

  1. 注解@After("切入点表达式")
  2. 调用时机:无论方法正常结束还是异常结束,都调用
    @After("execution(* com.changwu.tryspring.aop.MathAop.div(..))")
public void after(){
System.out.println("后置通知执行了!!!");
}

返回通知

  1. 注解 @AfterReturning(value="切入点表达式",returning="XXX")
  2. 调用时机: 方法正常返回执行
  3. 注意点: 函数的入参,参数名和注解中的XXX要相同,里面封装着,函数的返回值结果
    @AfterReturning(value = "com.changwu.tryspring.aop.AopLog.pointCut()",returning = "result")
public void returning(Object result){
System.out.println("切入点正常执行...运行结果..{"+result+"}");
}

异常通知

  1. 注解@AfterThrowing(value="切入点表达式",throwing="XXX")
  2. 调用时机: 切入点出现异常
  3. 注意点:函数的入参,参数名和注解中的XXX要相同,里面封装着,函数的返回值结果
  4. XXX里面封装着方法的异常信息
    @AfterThrowing(value = "com.changwu.tryspring.aop.AopLog.pointCut()",throwing = "expection")
public void afterThrowing(Exception expection){
System.out.println("切入点出现异常,异常结果..{"+expection+"}..");
}

环绕通知

  1. 注解@Around("切入点表达式")
  2. 调用时机: 切入点执行前后都会执行
  3. 参数:ProceedingJoinPoint里面封装了关于切入点的所有信息
  4. proceedingJoinPoint.proceed();返回值: 为切入点的返回值, 必须返回
  5. 环绕通知里面的所有异常全部抛出去,,一旦我们try起来了,异常通知就获取不到异常,进而返回通知就认为方法是正常结束的,结果为NULL
    @Around("com.changwu.tryspring.aop.AopLog.pointCut()")
public Object arounding(ProceedingJoinPoint proceedingJoinPoint) throws Throwable { System.out.println("环绕通知开始: 方法名: "+
proceedingJoinPoint.getSignature().getName() +
"将要被执行 参数为: "+Arrays.asList(proceedingJoinPoint.getArgs()));
Object result = proceedingJoinPoint.proceed();// 代理调用方法,如过调用方法add抛出异常,就不会执行后面的代码(所以要抛出去!)
// 调用方法之后执行
System.out.println("环绕通知,调用方法之后执行,获取的结果是:"+result);
return result; } }

注意点: 业务逻辑类,切面类都要添加进IOC

注意点: 切面类都要添加注解@Aspects

补充: JointPoint 可以获取的切入点的信息 而且,必须在参数的第一位


好继续准备,回顾xml版本的spring开发方式需要我们添加如下的配置

<aop:aspectj-autoproxy></aop:aspectj-autopeoxy>

基于注解,开启切面--->@EnableAspectjAutoProxy

@Configuration
@EnableAspectJAutoProxy
@ComponentScan("com.changwu.tryspring.aop")
public class MainAopConfig {
}

测试:

@Test
public void text14(){
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainAopConfig.class);
// 根据类型获取bean
MathAop mathAop = applicationContext.getBean(MathAop.class);
mathAop.div(2,1);
applicationContext.close();
}

Spring基础笔记的更多相关文章

  1. Spring Security笔记:HTTP Basic 认证

    在第一节 Spring Security笔记:Hello World 的基础上,只要把Spring-Security.xml里改一个位置 <http auto-config="true ...

  2. struts2,hibernate,spring整合笔记(2)

    上一话struts2,hibernate,spring整合笔记(1) 接下来继续 配置完struts之后就要开始hibernate的配置 hibernate的环境并不依赖web开发环境,在我第一次配置 ...

  3. spring学习笔记(一) Spring概述

    博主Spring学习笔记整理大部分内容来自Spring实战(第四版)这本书.  强烈建议新手购入或者需要电子书的留言. 在学习Spring之前,我们要了解这么几个问题:什么是Spring?Spring ...

  4. Java架构师之路 Spring学习笔记(一) Spring介绍

    前言 这是一篇原创的Spring学习笔记.主要记录我学习Spring4.0的过程.本人有四年的Java Web开发经验,最近在面试中遇到面试官总会问一些简单但我不会的Java问题,让我觉得有必要重新审 ...

  5. Spring基础知识

    Spring基础知识 利用spring完成松耦合 接口 public interface IOutputGenerator { public void generateOutput(); } 实现类 ...

  6. Java基础笔记 – Annotation注解的介绍和使用 自定义注解

    Java基础笔记 – Annotation注解的介绍和使用 自定义注解 本文由arthinking发表于5年前 | Java基础 | 评论数 7 |  被围观 25,969 views+ 1.Anno ...

  7. spring基础整理

    spring基础教程:https://www.tutorialspoint.com/spring/spring_overview.htm 注入实例 <bean id="" c ...

  8. Spring day03笔记

    spring day02回顾 AOP :切面编程 切面:切入点 和 通知 结合 spring aop 编程 <aop:config> 方法1: <aop:pointcut expre ...

  9. php代码审计基础笔记

    出处: 九零SEC连接:http://forum.90sec.org/forum.php?mod=viewthread&tid=8059 --------------------------- ...

随机推荐

  1. crontab 中curl命令无法正常执行

    这里所指curl无法执行Url情况是针对带参数的链接,方法体中无法获取参数的值. 比如: */7 * * * * curl http://localhost:8088/backening/sysOrd ...

  2. Bzoj 3124: [Sdoi2013]直径 题解

    3124: [Sdoi2013]直径 Time Limit: 10 Sec  Memory Limit: 256 MBSubmit: 1222  Solved: 580[Submit][Status] ...

  3. 【小家Spring】Spring IoC是如何使用BeanWrapper和Java内省结合起来给Bean属性赋值的

    #### 每篇一句 > 具备了技术深度,遇到问题可以快速定位并从根本上解决.有了技术深度之后,学习其它技术可以更快,再深入其它技术也就不会害怕 #### 相关阅读 [[小家Spring]聊聊Sp ...

  4. 后端开发工具:反编译工具、VS插件、.NET Framework源码地址

    再学习.工作中,开发免不了要使用第三方工具.今天介绍2款反编译工具 一.dnspy 免安装.免费.可调试.可修改重新编译dll 开源项目地址:https://github.com/0xd4d/dnSp ...

  5. Appium+python自动化(二十一)- 让猴子按你指令大闹手机,让你成为耍猴高手 - Monkey(猴子) - MonkeyScript(超详解)

    简介 一年一度的暑假如期而至,每年必不可少的,便是<西游记>这部经典电视连续剧的播出,作为一名90后,对于这部经典剧的情谊,就是观看已成为一种习惯.依然深刻的记得,小时候妈妈为了催促我睡觉 ...

  6. 【题解】旅行-C++

    Description 某趟列车的最大载客容量为V人,沿途共有n个停靠站,其中始发站为第1站,终点站为第n站.在第1站至第n-1站之 间,共有m个团队申请购票搭乘,若规定:(1)对于某个团队的购票申请 ...

  7. .NET CORE 多语言实现方案

    根据市场需求,基于.NET CORE平台开发的RoadFlow工作流平台需要实现多语言版本.经过方案讨论和比对,决定采用.NET自带的本地化功能来实现多语言.话不多说,直接上实现方式. 首先修改Sta ...

  8. java中this 和 super关键字的作用

    emmmmmm也真的是好久没有写过java了,因为项目需要, 最近又必须重新拾起来了,虽然好多东西也都忘得差不多了.... 然后发现 竟然把super和this傻傻分不清.... 开个帖子记录一下: ...

  9. 个人永久性免费-Excel催化剂功能第76波-图表序列信息维护

    在之前开发过的图表小功能中,可以让普通用户瞬间拥有高级图表玩家所制作的精美图表,但若将这些示例数据的图表转换为自己实际所要的真实数据过程中,仍然有些困难,此篇推出后,再次拉低图表制作门槛,让真实的数据 ...

  10. 个人永久性免费-Excel催化剂功能第64波-多级数据如省市区联动输入,自由配置永不失效

    日常使用各大系统过程中,数据录入的规范性一般做得都很不错,本来系统的存在很大范畴就是为了数据和管理的规范性.在Excel环境中,想得到规范性的数据录入,除非是自行对数据有很深的认识,知道哪些数据是脏乱 ...