提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
目录
前言
本篇文章主要将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;
}
}