纯spring注解

85 阅读8分钟

@Configuration类注解等同于xml配置文件配置类

@ComponentScan 包扫描注解相当于<context:component-scan base-package="com.itheima"/>

注意:扫描的包路径不写,扫描当前包及其子包!

零配置IOC容器

加载纯注解格式上下文对象,需要使用AnnotationConfigApplicationContext // SpringConfig是一个配置类(有@Configuration注解)

ApplicationContext ctx = new AnnotationConfigApplicationContext(SpringConfig.class); 
   
   @Service @Controller 用在指定的类上,用来替代bean标签。

@Autowired 用在需要注入的属性上,完成自动属性注入。

@Configuration 用在配置类上,用来替代spring核心配置xml文件。

@ComponentScan("com.itheima") 用来进行注解包的扫描

new AnnotationConfigApplicationContext(SpringConfig.class) 用于创建注解spring容器。

Spring整合Druid

@Bean注解

  • 类型:方法注解
  • 作用:将方法的返回值存储到Spring IOC容器中
  • @Bean("user1") bean的id可以指定,默认是方法名 作用:将方法的返回值存储到Spring IOC容器中

public User user(){ return new User(1,"xiaoming",new Date(),"male","china"); } 相当于XML配置文件中的标签

@Value注解

作用:注入非引用类型,设置属性的值或对方法进行传参 @Value("${jdbc.username}") private String username;

  • @value一般用于读取properties文件中的属性值
  • 注解的方式注入非引用类型,不需要setter方法

@Import注解

  • 类型:类注解 作用:导入其他配置类(对象) //RedisConfig是另外一个配置类 @Import(RedisConfig.class) public class SpringConfig{

} 相当于XML配置文件中的<import>标签 注意:也可以直接将配置类放在注解扫描器可以扫描的位置,就不需要再使用@Import注解导入了。

@PropertySource注解 引入外部属性文件(jdbc.properties)

相当于XML中<context:property-placeholder location="classpath:jdbc.properties"/> 完善 JdbcConfig 类的功能

1:使用注解形式完成外部属性文件(jdbc.properties)的引入。参考注解:@PropertySource

2: 将引入的文件中的属性注入到 成员变量上。参考注解: @Value注解

3: 将获取数据库连接池方法返回值,当做bean教给Spring管理.参考注解@Bean。

4: SpringConfig配置类 整合 JdbcConfig配置类。参考注解@Import。

5: 完成Test02测试,从Spring容器中获取连接池对象。

JdbcConfig.java
就是 获取了一个德鲁伊连接池对象
  • 有几个问题要思考
    
  •   1: 配置属性信息如何完成赋值?
    
  •   2: 连接池对象如何教给spring管理
    

//创建数据源对象并返回 DruidDataSource druidDataSource = new+++ DruidDataSource(); @PropertySource 用在配置类上,用于解析配置文件。

@Value 用在指定成员变量上,用于获取配置文件属性。

@Import(JdbcConfig.class) 用在主要配置类上,用来引入其他配置类。 配置初始化Spring容器的配置文件(配置类)

  • @RunWith(SpringJUnit4ClassRunner.class)
  • 注解作用:spring的单元测试运行器替换原有运行器
  • @ContextConfiguration(classes = SpringConfig.class)
  • 注解作用:指定配置类或者配置文件所在位置
  • classes属性:设置配置类
  • locations属性:设置配置文件 */

AOP - 面向切面编程--对管理的bean进行功能增强

  • 怎么把横切逻辑再融合到原有业务逻辑上,达到和原来一样的效果?植入

这些功能最终是通过代理对象来封装。代理对象功能=代理增强功能+原对象原功能。

**Joinpoint(连接点)** : 所谓连接点是指那些被拦截到的点。在spring中,这些点指的是方法,因为spring只支持方法类型的连接点。

举例:所有的方法都是连接点

Pointcut(切入点) : 所谓切入点是指我们要对哪些Joinpoint进行拦截的定义。

举例:需要增加事务功能,日志功能的方法都是切入点

Advice(通知) : 所谓通知是指拦截到Joinpoint之后所要做的事情就是通知

通知的类型:前置通知,后置通知,异常通知,最终通知,环绕通知。

举例:增加的事务和日志功能就是 通知。

Introduction(引介) 【了解】: 引介是一种特殊的通知在不修改类代码的前提下, Introduction可以在运行期为类动态地添加一些方法或Field。

Target(目标对象) :代理的目标对象。

举例:AccountServiceImpl类需要增加 事务和日志功能,他就是目标对象

Weaving(织入) : 是指把增强应用到目标对象来创建新的代理对象的过程。 spring采用动态代理织入,而AspectJ采用编译期织入和类装载期织入。

举例:把事务和日志功能动态添加到AccountServiceImpl对象的过程就是织入。

Proxy(代理) : 一个类被AOP织入增强后,就产生一个结果代理类。

举例:AccountServiceImpl中的方法 得到了事务和日志增强,就生成了代理对象。

Aspect(切面) : 是切入点和通知(引介)的结合。

举例:事务方法 加上 事务功能 就是一个切面

案例方法的执行效率currentTimeMillis

##### 创建一个新的类,将共性功能提取出来制作成独立的方法

AOPAdvice 通知类

 步骤3:在新的类中定义一个方法,无参无返回值,方法体为空。上面添加一个注解@Pointcut
  public class AOPAdvice {//只对你有感觉UserServiceImpl
// //    设置连接点 上有切入点  准备切谁
    @Pointcut("execution( * com.itheima.service.impl.*.*(..))")
    public void pc(){}

}
 //Pointcut(切入点): 所谓切入点是指我们要对哪些Joinpoint进行拦截的定义。
  ##### 步骤4:绑定原始操作与被抽取的功能之间的关系
   public class AOPAdvice {
    @Pointcut("execution(void com.itheima.service.impl.UserServiceImpl.save(..))")
 //标准格式:动作关键字(访问修饰符 返回值 包名.类/接口名.方法名(参数)异常名)
    private void pt() {
    }
    @Before("AOPAdvice.pt()")
    public void before(){
        System.out.println("功能执行前系统时间:" + System.currentTimeMillis());
    }
     @After("pt()")
    public void afterSave(){
        System.out.println("功能执行后系统时间:" + System.currentTimeMillis());
} 
  ##### 步骤5:声明切面类
@Component 把当前类存入spring容器
@Aspect
Aspect(切面): 是切入点和通知(引介)的结合。 
	举例:事务方法 加上 事务功能 就是一个切面 
public class AOPAdvice {
}   
##### 步骤6:在Spring配置类上声明开启AOP功能
++++@Configuration 配置类
@ComponentScan("com.itheima")包扫描
@Enable开启AspectJAutoProxy
public class SpringConfig {
}
没有@EnableAspectJAutoProxy这个注解,注解是AOP是无效的,千万记得,很容易忘记写这个 
     // 举例:需要增加事务功能,日志功能的方法都是切入点
    
    execution是执行的意思,整体含义定义了一个执行位置,这个位置是void返回值,,包名是com.itheima.service.impl这个,类名是UserServiceImpl,方法名是save,没有参数的方法。 
    参数也可以,携带两个参数可以写成( * , * ),携带两个参数,第一个int,第二个任意,就写成 ( int , * )

+:专用于匹配子类类型 这个使用率较低,描述子类的,

0.  包名书写尽量不使用..匹配,效率过低,常用*做单个包描述匹配,或精准匹配
0.  通常不使用异常作为匹配规则

起别名 @AliasFor("a2") String a1() default ""; @AliasFor("a1") String a2() default "";

之前xml获取

    
@Test
public void beanTest() {
    ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
    // 从容器中获取  account对象
    // account对象获取有三种形式 要求掌握一种
    Object account = context.getBean("account");
    // 第二种方式
    Account bean = context.getBean(Account.class);
    // 第三种方式  我们建议使用第三种
    Account account1 = context.getBean("account", Account.class);
    System.out.println("我是debug看对象的");

}

- 环绕通知 @Around 追加功能到方法执行前后(重要)

   -   环绕通知 @Around 追加功能到方法执行前后(重要)
-   位置:通知方法定义上方
-   属性value(默认):切入点方法名,格式为类名.方法名()
    @Around("AOPAdvice.pt()")
public Object around(ProceedingJoinPoint pjp) throws Throwable {
    System.out.println("环绕前around before...");
    Object ret = pjp.proceed();
    System.out.println("环绕后around after...");
    return ret;
}
    0.  环绕通知必须依赖形参ProceedingJoinPoint才能实现对原始方法的调用,进而实现原始方法调用前后同时添加通知
0.  对原始方法的调用可以不接收返回值,通知方法设置成void即可,如果接收返回值,必须设定为Object类型
0.  原始方法的返回值如果是void类型,通知方法的返回值类型可以设置成void,也可以设置成Object
0.  由于无法预知原始方法运行后是否会抛出异常,因此环绕通知方法必须抛出Throwable对象
  @Around("pt()")
public Object around(ProceedingJoinPoint pjp) throws Throwable {
    //获取执行签名信息
    Signature signature = pjp.getSignature();
    //通过签名获取执行类型(接口名)
    String className = signature.getDeclaringTypeName();
    //通过签名获取执行操作名称(方法名)
    String methodName = signature.getName();
    //执行时长累计值
    long sum = 0L;
    for (int i = 0; i < 10000; i++) {
        //获取操作前系统时间beginTime        
        long startTime = System.currentTimeMillis();
        //原始操作调用
        pjp.proceed();
        //获取操作后系统时间endTime
        long endTime = System.currentTimeMillis();
        sum += endTime-startTime;
    }
    System.out.println(className+":"+methodName+" (万次)run:"+sum+"ms");
    return null;
}  
 这段程序中加了一个东西,使用ProceedingJoinPoint对象可以获得一个Signature对象。 
 通过Signature这个东西可以拿到当前执行的类、方法,这样最后打印输出的时候就能区分出

-   JoinPoint:适用于前置、后置、返回后、抛出异常后通知
-   ProccedJointPoint:适用于环绕通知                             
  只要见到字符串,先去掉输入的数据中前后的无效空格数据。

trim()```操作去掉空格args[i] = args[i].toString().trim();,
pjp.proceed()```可以得到返回值

环绕通知最强大的地方,不仅可以修改参数,还可以修改返回值。

//调用原始操作,使用修改后的参数替换原始参数 Object ret = pjp.proceed(args);                              
 @Around先获取参数,然后调用的时候修改参数就可以了 
@Around("pt()")
public Object around(ProceedingJoinPoint pjp) throws Throwable {
    Object[] args = pjp.getArgs();
    //对原始参数的每一个参数进行操作
    for (int i = 0; i < args.length; i++) {
        //如果是字符串数据
        if(args.getClass().equals(String.class)){
            //取出数据,trim()操作后,更新数据
            args[i] = args[i].toString().trim();
        }
    }
    //调用原始操作,使用修改后的参数替换原始参数
    Object ret = pjp.proceed(args);
    return ret;
}                              

image.png