01 Spring中ioc和aop详解

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档


前言

本篇文章主要将Spring的基本特点以及整合其他框架的方法。Spring在广义上是指spring全家桶,狭义上指SpringFramework,是一个简洁的,方便整合其他框架的框架。本篇就就狭义来讲spring的概念和使用。

一、spring是什么?

spring是J2EE应用程序框架,是轻量级的IOC和AOP的容器框架,主要针对javaBean的生命周期进行管理的轻量级容器,可以单独使用,也可以和其他框架组合使用。

1.1 IOC概念

        IOC又称控制反转,是指创建对象的主动权的从以前通过手动new 转移到的spring容器中; spring根据配置文件自动创建对象并放到 IOC容器中,当我们用时直接去IOC 容器中取;

        IOC是一个抽象的概念性容器,并非真是存在,在项目启动的时候,就将要使用的类的对象——bean,创建出来并放到到了IOC容器中;将bean对象注入IOC容器的方式有三种: 构造器注入, setter方法注入 ,根据注解注入;

1.2 AOP概念

        AOP又称面向切面编程、无侵入式编程,作用是在不改变原有代码的基础上增加功能。

将公共行为和逻辑抽取并封装为一个可以重用的模块, 这个模块被称为切面;

二、spring详细介绍

1、 IOC注解开发

        注解开发的加载流程就是,首先加载配置类,在配置类上指定扫描路径,扫描指定的包中都有哪些注解,然后将扫描到添加@Component注解的类加载bean对象到IOC容器。

1.1 使用注解定义bean

        使用注解定义bean,就是在类上添加一个注解实现自动将类的对象创建出来并放入了IOC容器。

  注解:@Component、@controller、@Service、@Repository;添加在类上,是将类的bean对象

1.2 配置类

        注解开发不在使用XML文件,xml文件由配置类所代替。

配置类上注解的使用

1.添加配置类,在配置类上使用这两个标签;

@Confinguration
@CompinentScan("com.itcast")
public class SpringConfig{}

    @Configuration    //指定这个类是配置类;
    @CompinentScan("com.itheima")    //指定扫描路径,用于加载使用注解格式定义的bean,此注解只能添加一次,如果想要指定多个路径,就使用数组的格式;
    
2.获取对象的接口改为AnnotationConfigApplicationContext()
    ApplicationContext ctx = new AnnotationConfigApplicationContext(SpringConfig.class);
    //括号里面填的是配置类名.class;

1.3 bean的作用范围以及生命周期

1.Bean的作用范围设置
    @Scope("prototype")        //指定该类创建对象的作用范围
            //定义在类的上方,默认为单例singleton,可以选值为prototype;
2.Bean的生命周期设置
    @PostConstruct //方法注解,位置在方法上,指定在构造方法之后执行,执行顺序
Constructor(构造方法) —> @Autowired(依赖注入) —> @PostConstruct(注释的方法)

    @PreDestroy //方法注解,位置在方法上,指定在销毁方法之前执行
    
  
 注意:从JDK9以后jdk中这两个注解的包就被移除了,如果@PostConstruct和@PreDestroy注解如果找不到,需要我们从外部导包:
        <dependency>
          <groupId>javax.annotation</groupId>
          <artifactId>javax.annotation-api</artifactId>
          <version>1.3.2</version>
        </dependency>

1.4 bean注入

概念:前面已经讲了讲bean对象放入IOC容器,依赖注入就是将容器中的bean对象拿出来使用;

注入引用类型的bean:

1.格式:

    public class UserServiceImpl implements UserService {
        @Autowired  //指定根据类型自动注入依赖
        @Qualifier("bean对象名称") //指定根据bean的名注入依赖;
        private UserDao userDao; //属性
    }

2.@Autowired 属性注解,用来指定根据类型自动注入依赖
    -可以写在属性上,也可也写在setter方法上,最简单的处理方式是写在属性上并将setter方法删除掉;
    -默认按照类型自动装配,如果IOC容器中同类的Bean找到多个,就按照变量名和Bean的名称匹配;
    -容器中有多个bean,注入参数的属性名又和容器中bean的名称都不一致时,就需要使用到@Qualifier;
3.@Qualifier("") 属性注解,用来指定跟据名称自动注入依赖
    -@Qualifier不能独立使用,必须和@Autowired一起使用

注入简单类型的bean:

1.@value("要添加给属性的值")
    属性注解,定义在属性上,为简单类型对象添加依赖;
  格式:

    public class UserDaoImpl implements UserDao {
        @Value("itheima")
        private String name;
    }

1.5 读取Properties文件

步骤:

1.在配置类上使用@pertySource注解加载properties文件

@Configuration
@ComponentScan("com.heima")
@PropertySource("jdbc.properties") //指定了路径,我们可以取这个文件中的值
public class SpringConfig {
}

2.取值:将properties文件中的值取出,赋给指定属性
    格式:用${}取值,并且只能在@Value("")中使用才有效

public class UserDaoImpl implements UserDao {
    @Value("${jdbc.username}")
    private String username;
}

3.注意:
    a.如果用@PropertySource的属性读取的properties配置文件有多个,可通过数组的格式来指定;
        @PropertySource({"jdbc.properties","xxx.properties"})
    b.@PropertySource注解属性中不支持使用通配符*,运行会报错;
        @PropertySource({"*.properties"})    错误
    c.@PropertySource注解属性中可以把classpath:加上,代表从当前项目的根路径找文件;
        @PropertySource({"classpath:jdbc.properties"})

1.6 管理第三方Bean

        第三方的类.我们没办法直接在类上方添加注解,这时,就需要我们去在配置类中在一个方法内部New出第三方类的对象,方法的返回值是第三方类的对象;在方法上方添加@Bean注解,实现将返回的对象存入IOC容器;

例如管理druid数据库链接池类:

1.在pom.xml中导入Druid资源包的依赖,导入第三方类
    <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>druid</artifactId>
        <version>1.1.16</version>
    </dependency>
2.在配置类中添加一个方法,在方法上添加@Bean注解

@Configuration
    public class SpringConfig {
        @Bean("dataSource")
        public DataSource dataSource(){
            DruidDataSource ds = new DruidDataSource();
            ds.setDriverClassName("com.mysql.jdbc.Driver");
            ds.setUrl("jdbc:mysql://localhost:3306/spring_db");
            ds.setUsername("root");
            ds.setPassword("root");
            return ds;
        }
    }

3.从IOC容器中获取对象并打印

public class App {
        public static void main(String[] args) {
            AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(SpringConfig.class);
            DataSource dataSource = ctx.getBean(DataSource.class);
            System.out.println(dataSource);
        }
    }


--@Bean注解:属于方法注解,在方法上面使用
    作用:将方法的返回值制作为Spring管理的一个bean对象,id默认是方法名,括号里面给@Bean("")自定义id;
--当注入引用类型的对象时,直接将需要的对象写到方法形参上,会自动根据类型加载;

1.7 引入外部配置类

除了SpringConfig配置类,我们还会用到外部配置类,如何实现外部配置类的加载呢?

例如加载JdbcConfig外部数据源配置类

新建一个JdbcConfig配置类作为外部配置类,并把数据源配置到该类下;

public class jdbcConfig {
        @Bean("dataSource")	//实现给外部类配置Bean对象
        public DataSource getDataSource(){
            DruidDataSource ds = new DruidDataSource();
            ds.setDriverClassName("com.mysql.jdbc.Driver");
            ds.setUrl("jdbc:mysql://127.0.0.1:3306/db12");
            ds.setUsername("root");
            ds.setPassword("root");
            ds.setInitialSize(5);
            ds.setMaxActive(10);
            ds.setMaxWait(3000);
            return ds;
        }
    }

将外部配置类引入的犯非法的有两种:

    第一种:
        1.在JdbcConfig外部配置类上添加配置注解@Configuration;
        2.在Spring的配置类上添加包扫描,扫描外部配置类所在的包
            @Configuration
             @ComponentScan("com.itheima.config")
             public class SpringConfig {
             }

    第二种:在Spring配置类中引入外部配置类的class
        @Configuration
         @Import({JdbcConfig.class})
         public class SpringConfig {
        }

        注意:-@Import注解在配置类中只能写一次;
                -@Import参数需要的是一个数组,可以引入多个配置类;

 2. AOP

        概念 : 面向切面编程
        作用 : 在不改变原有代码基础上增加功能
        理念 : 无入侵式编程

2.1 AOP核心概念:

  • 连接点(JoinPoint):程序执行过程中的任意位置,粒度为执行方法、抛出异常、设置变量等
        -在SpringAOP中,理解为方法的执行
  • 切入点(Pointcut):匹配连接点的式子
        -在SpringAOP中,一个切入点可以描述一个具体方法,也可也匹配多个方法
        -一个具体的方法:如com.itheima.dao包下的BookDao接口中的无形参无返回值的save方法
        -匹配多个方法:所有的save方法,所有的get开头的方法,所有以Dao结尾的接口中的任意方法,所有带有一个参数的方法
        -连接点范围要比切入点范围大,是切入点的方法也一定是连接点,但是是连接点的方法就不一定要被增强,所以可能不是切入点。
  • 通知(Advice):在切入点处执行的操作,也就是共性功能
        -在SpringAOP中,功能最终以方法的形式呈现
  • 通知类:定义通知的类
  • 切面(Aspect):描述通知与切入点的对应关系。

2.2 AOP入门案例

第一步:环境准备

1.在pom.xml文件中导入依赖坐标
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>5.2.10.RELEASE</version>
    </dependency>
    
2.制作连接点方法(原始的操作,是个Spring的的项目就行)

第二步:编写切面类

在切面类中完成定义切入点;创建通知;绑定切入点与通知;

//1.创建一个类,在里面配置切入点,切面,通知
@Component
@Aspect //2.标识这个类为切面类(通知类)
public class MyAdvice {
    //4.定义切入点
    @Pointcut("execution(void com.itheima.dao.UserDao.save())")
    private void qrd(){
    }

    //5.绑定通知与切入点
    @Before("qrd()")
    //3.创建一个通知
    public void tongZhi(){
        System.out.println(System.currentTimeMillis());
    }
}
@Aspect:类注解;定义在切面类的上方;设置当前类为AOP切面类
@Pointcut:方法注解,定义在切入点方法上方;设置切入点方法;
    格式:Pointuct("execution(void com.itheima.dao.UserDao.save())")
@Before:方法注解;定义在通知方法上方;设置当前通知方法与切入点之间的绑定关系,

第三步: 在SpringConfig主配置类中开启注解格式AOP

@Configuration
@ComponentScan("com.itheima")
@EnableAspectJAutoProxy //6.提示开启注解格式AOP功能
public class SpringConfig {
}
@EnableAspectJAutoProxy:配置类注解;定义在配置类上方;开启注解格式AOP功能
    @EnableAspectJAutoProxy(proxtargetClass = true) 默认为false
    proxtargetClass=true 不论目标有没有接口都采用cglib动态代理
    proxtargetClass=false 看目标有没有接口,有则采用jdk动态代理,没有就采用cglib动态代理

2.3 AOP工作流程原理

2.4 切入点表达式

        概念 : 描述切入点的式子

        格式 : 我们可以描述接口中的方法,也可以描述实现类中的方法,等效;

execution(public User com.itheima.service.UserService.findById(int)) 
动作关键字(访问修饰符 返回值 包名.类/接口名.方法名(参数) 异常名) 
详解:
动作关键字: execution,描述切入点的行为动作,例如execution表示执行到指定切入点 
访问修饰符: public:还可以是public,private等,可以省略 
返回值: User写返回值类型 
包名: com.itheima.service 多级包使用点连接 
类/接口名称: UserService 
方法名: findById 
参数: int,直接写参数的类型,多个类型用逗号隔开 
异常名:方法定义中抛出指定异常,可以省略

        通配符 :

 书写技巧:

2.5 通知

概念 : 用不同的注解将共性代码加到原有代码的指定位置;

通知的类型:

@Before        当前通知方法在原始切入点方法前运行
@After        当前通知方法在原始切入点方法后运行
@AfterReturning    当前通知方法在原始切入点方法正常执行完毕后执行
@AfterThrowing    当前通知方法在原始切入点方法运行抛出异常后执行
@Around        当前通知方法在原始切入点方法前后运行
    格式:
    @Around("pt2()")
    public Object aroundSelect(ProceedingJoinPoint pjp) throws Throwable {
    System.out.println("around before advice ...");
        //表示对原始操作的调用,也是指定的原始方法的运行位置
        Object ret = pjp.proceed();
        System.out.println("around after advice ...");
        return ret;
    }

@Around注意事项:
    1. 环绕通知必须依赖形参ProceedingJoinPoint才能实现对原始方法的调用,进而指定原始方法的执行位置
    2. 通知中如果未使用ProceedingJoinPoint对原始方法进行调用将跳过原始方法的执行
    3. 对原始方法的调用可以不接收返回值,通知方法设置成void即可,如果接收返回值,最好设定为Object类型
    4. 原始方法的返回值如果是void类型,通知方法的返回值类型可以设置成void,也可以设置成Object
    5. 由于无法预知原始方法运行后是否会抛出异常,因此环绕通知方法必须要处理Throwable异常

2.6 模拟百度验证码对AOP的使用

目标: 百度会对验证码进行登录验证,使用切面编程在登录前将验证码去除空格;  

//1.导入依赖
//    <dependency>
//        <groupId>org.springframework</groupId>
//        <artifactId>spring-context</artifactId>
//        <version>5.2.10.RELEASE</version>
//    </dependency>

//2.创建dao层,service层的接口和实现类
    public interface BDYZDao {
        boolean yanZheng(String s);
    }
    @Repository
    public class BDYZDaoImpl implements BDYZDao {
        @Override
        public boolean yanZheng(String s) {
            boolean b = s.equals("root");
            return b;
        }
    }
    public interface BDYZService {
        boolean yanZheng(String s);
    }
    @Service
    public class BDYZServiceImpl implements BDYZService {
        @Autowired  //1.指定案类型自动装配
        private BDYZDao bdyzDao;
        @Override
        public boolean yanZheng(String s) {
            System.out.println("验证");
            //2.调用Dao层的方法,接受返回值并再返回
            return bdyzDao.yanZheng(s);
        }
    }
//3.编写业务类实现类,对验证码进行比较验证
	@Service
    public class BDYZServiceImpl implements BDYZService {
        @Autowired  //1.指定案类型自动装配
        private BDYZDao bdyzDao;
        @Override
        public boolean yanZheng(String s) {
            System.out.println("验证");
            //2.调用Dao层的方法,接受返回值并再返回
            return bdyzDao.yanZheng(s);
        }
    }
//4.编写测试类,调用执行service层的验证方法
	public class Test {
        public static void main(String[] args) {
            //1.获取容器
            AnnotationConfigApplicationContext axt = new AnnotationConfigApplicationContext(SpringConfig.class);
            //2.从容器中获取service层对象
            BDYZService service = axt.getBean(BDYZService.class);
            //3.调用service层的方法
            boolean b = service.yanZheng("root ");
            System.out.println("验证是否成功:"+b);
        }
    }
//5.编写切面类,实现在对验证码校验之前去除空格,
	@Component
    @Aspect //1.指定这是切面类
    public class MyAdvice {
        //2.指定切入点
        @Pointcut("execution(* com.baiDuYanZhengMa.service.BDYZService.*(*))")
        private void pt(){}
        //3.在通知上,建立切入点与通知的连接关系
        @Around("pt()")
        public Object method(ProceedingJoinPoint pjp) throws Throwable {
            System.out.println("Before");
            //1.获取参数
            Object[] args = pjp.getArgs();
            //2.加工参数。传递多个参数,以数组的格式接受,提取第一个参数转成字符串并去除空格。
            String trim = args[0].toString().trim();
            args[0]=trim;
            //3.执行原代码并传入修改后的参数,并获取返回值
            Object p = pjp.proceed(args);
            System.out.println("after");
            //4.将返回值返回
            return p;
        }
    }

 

  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值