目录
一,什么是Spring?
二,控制反转(IOC)
2.1 控制反转是什么?
2.2 如何实现控制反转?
2.3 依赖注入
2.3.1 set注入简单类型
2.3.2 注入Bean
2.3.3 注入数组
2.3.4 注入List集合
2.3.5 注入Set集合
2.3.6 注入Map集合
2.3.7 注入Properties
2.4 Bean的作用域
2.5 IOC的注解式开发
2.5.1声明Bean的注解常见的有四个
2.5.2 spring给出四个注解用来给属性赋值:
2.6 IOC的全注解开发
三,AOP
3.1:AOP中的名词解释
3.2切入点表达式
3.3 基于AspectJ的AOP注解式开发
3.4 通知类型
3.4.1 通知的执行顺序
3.4.2 有多个切面时,切面的执行顺序
3.5全注解式开发AOP
3.6 AOP的XML开发(了解)
百度百科这样形容Spring:Spring框架是一个开放源代码的J2EE应用程序框架,由Rod Johnson发起,是针对bean的生命周期进行管理的轻量级容器(lightweight container)。 Spring解决了开发者在J2EE开发中遇到的许多常见的问题,提供了功能强大IOC、AOP及Web MVC等功能。Spring可以单独应用于构筑应用程序,也可以和Struts、Webwork、Tapestry等众多Web框架组合使用,并且可以与 Swing等桌面应用程序AP组合。因此, Spring不仅仅能应用于J2EE应用程序之中,也可以应用于桌面应用程序以及小应用程序之中。Spring框架主要由七部分组成,分别是 Spring Core、 Spring AOP、 Spring ORM、 Spring DAO、Spring Context、 Spring Web和 Spring Web MVC
总之:Spring是一个轻量级的控制反转(IOC)和面向切面(AOP)的框架。
下面分别演示两种注入方法:
先创建一个Maven工程:
然后无脑下一步即可。
这时来准备我们的依赖,因为是maven工程,所以这里只给出坐标
junit
junit
4.11
test
org.springframework
spring-context
5.3.22
这里引入spring-context后会关联引入一些其他的jar包,如下图:
然后就可以写代码了。
构造方法注入是spring容器通过构造方法来给属性赋值、所以类必须有构造方法
先写一个实体类:
package com.hkd.Bean;
public class user {
String name;
int age;
public user() {
}
public user(String name, int age) {
this.name = name;
this.age = age;
}
public void setName(String name) {
this.name = name;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "user{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
写好实体类后就可以 编写Spring配置文件了。
在IDEA中文件的目录结构如下:
spring配置文件如下:
运行后可看到结果:
这里constructor-arg标签中的index属性指的是,参数在构造方法中的顺序。
除了index可以指定参数外也可用参数的名称来指定:
如果需要的参数是引用类型则按照下面写法:
简单类型包括:
简单类型注入的代码大致为:
在这里有几个标签:
先创建一个实体类,提供set和构造方法
package com.hkd.Bean;
public class user {
String name ;
int age;
public user() {
}
public user(String name, int age) {
this.name = name;
this.age = age;
}
public void setName(String name) {
this.name = name;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "user{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
spring配置文件如下:
到这里准备工作完成可以进行set注入的测试了:
写一个测试类如下:
package myTest;
import com.hkd.Bean.user;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class DiTest {
@Test
public void TestSetDI(){
ApplicationContext ioc = new ClassPathXmlApplicationContext("applicationContext.xml");
user user = ioc.getBean("user", user.class);
System.out.println(user);
}
}
控制台可看到结果为:
简单类型注入成功。
如果在一个对象在另一个对象中作为一个属性,那么在注入的时候就不能用和简单类型一样的方法进行注入了,此时的注入格式如下:
这里可以看到相比于注入简单类型,注入Bean知识将value=""换为了ref="",此时ref=""中的值应填写为一个Bean的id就是spring容器管理的一个对象的名称。
如在刚才的程序中添加一个Vip类,类中有一个user属性,则可以按照一下方式写:
VIP类:
package com.hkd.Bean;
public class Vip {
int id;
int balance;
user user;
public Vip() {
}
public Vip(int id, int balance) {
this.id = id;
this.balance = balance;
}
public void setUser(com.hkd.Bean.user user) {
this.user = user;
}
public void setId(int id) {
this.id = id;
}
public void setBalance(int balance) {
this.balance = balance;
}
@Override
public String toString() {
return "Vip{" +
"id=" + id +
", balance=" + balance +
", user=" + user +
'}';
}
}
Spring配置文件:
在控制台可以看到user1对象被成功注入到了Vip了中。
除了这种方式还有一种注入Bean的方式:
当输入的数组类型是简单类型时:
抽烟
喝酒
烫头
非简单类型时:
抽烟
喝酒
烫头
注:当List集合中是简单类型时使用value标签反之使用ref标签
抽烟
喝酒
烫头
注:当Set集合中是简单类型时使用value标签反之使用ref标签
要点:
com.mysql.cj.jdbc.Driver
jdbc:mysql://localhost:3306/spring
root
123456
Spring创建的对象默认是单例的。
如果想让对象是多例的可以在指定scope的属性值为prototype,这样每调用一次getBean方法都会新创建一个对象。
默认情况下scope的值为singleton,即为单例
这四个注解中@Controller,@Service,@Repository是@Component的别名, 四个注解用法相同,对程序来说这三个都是一样的,那么为什么还要搞三个别名呢--这是给程序员看的,为了提高高代码的可读性。
这里拿@Component注解举例,其他三个用法相同:
@Component注解写在类上
package com.hkd.Bean;
import org.springframework.stereotype.Component;
@Component(value = "person")
public class preson {
String name;
}
这里的value值就是spring容器在创建对象时的对象名,可以忽略不写,此时对象名为首字母小写的类名
当然仅仅在类中写上注解还不行,spring容器怎么样才能发现,或者说spring容器怎么判断程序员是否写了注解呢?
这时要在spring的核心配置文件中告诉spring容器去哪些包中找带有注解的类,即需要添加包扫描,spring容器会到对应的包中检查是否写了注解,如果写了注解,那就创建对象。
如果是多个包有两种解决⽅案:
此时需要添加context命名空间
至此对象就可以成功创建了,那有了对象要怎么给属性赋值呢?
@Value : 当属性的类型是简单类型时使用@Value进行注入,
@Value可以写在属性上,构造方法里,set方法上,且当@Value写在属性上时可以不提供set方法
@Autowried :当属性的类型是引用类型时使用@Autowried注入(按类型注入)
@Autowried注解可以标注在哪⾥?
该注解有⼀个required属性,默认值是true,表示在注⼊的时候要求被注⼊的Bean必须是 存在的,如果不存在则报错。如果required属性设置为false,表示注⼊的Bean存在或者不存在都没 关系,存在的话就注⼊,不存在的话,也不报错。
因为@Autowried是按类型注入的,所以如果在spring容器中有多个相同类型的对象的话,@Autowried在注入时就会发生混乱,此时程序会报错。
这里的bigBoss和littleBoss是Boss的实现类
当我们进行注入的时候就会报错,发现了两个对象。
有没有办法解决这个问题呢?
肯定是有的,还有一个@Qualifier,这两个注解进行搭配使用就可以根据名称进行注入。
我们将上面代码稍加改动
此时再运行代码就不会出错且在控制台输出:
@Resource:@Resource也可以完成⾮简单类型注⼊。
那它和@Autowired注解有什么区别?
javax.annotation
javax.annotation-api
1.3.2
IOC的全注解开发就是不再写XML配置文件了,而是写一个类来代替XML配置文件
package com.hkd.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
@Configuration
@ComponentScan("com.hkd.Bean")
public class SpringConfig {
}
这时候我们不再创建ClassPathXmlApplicationContext对象了
除了创建的对象不一样之外,其余操作与XML写法一致。
什么是切入点表达式:就是表明你想要在哪个方法上织入切面(添加代码,增强功能)
execution([访问控制权限修饰符] 返回值类型 [全限定类名]⽅法名(形式参数列表) [异常])
先写一个目标类:
package com.hkd.AspectJ.Service;
import org.springframework.stereotype.Service;
@Service
public class orderService {
public void Buy(){
System.out.println("买东西...");
}
}
再写一个切面类:
package com.hkd.AspectJ.aspect;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;
@Component
@Aspect
public class myAspect {
@Before("execution(* com.hkd.AspectJ.Service.*.*(..))")
public void myBefore(){
System.out.println("前置通知执行了...");
}
}
Spring.xml:
这里看到在spring.xml文件中多了这样的一行代码
这句的作用是开启aop的自动代理添加完这句代码后所有带有@Aspect注解的都会生成一个代理对象
测试:
通知类型包括:
@Pointut注解:可以写在一个方法上作为一个切点表达式
@Pointcut("execution(* com.hkd.AspectJ.Service.*.*(..))")
public void pointCut(){}
此时pointCut()等同于execution(* com.hkd.AspectJ.Service.*.*(..))
此时我们来看无异常时通知的执行顺序:
package com.hkd.AspectJ.aspect;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
@Component
@Aspect
public class myAspect {
@Pointcut("execution(* com.hkd.AspectJ.Service.*.*(..))")
public void pointCut(){}
@Before("pointCut()")
public void myBefore(){
System.out.println("前置通知执行了...");
}
@AfterReturning("pointCut()")
public void myAfterReturning(){
System.out.println("后置通知执行了...");
}
@Around("pointCut()")
public void myAround(ProceedingJoinPoint point) throws Throwable{
System.out.println("环绕通知开始...");
point.proceed();
System.out.println("环绕通知结束...");
}
@After("pointCut()")
public void myAfter(){
System.out.println("最终通知执行了...");
}
}
由此可以看出最先执行的是环绕通知,然后是前置通知,在执行目标方法,紧接着后置通知,最终通知。
当有异常时:
我们在切面类中添加异常通知,这里不做演示
此时再进行测试:
从结果中我们可以看到,程序到达错误语句时报错且除了最终通知外后面的通知不再执行, 由此可见最终通知的作用类似finally代码块,这里可以写一些必须执行的代码。
要搞清楚切面的执行顺序,我们先来看一个注解@Order,@Order注解可以指定一个整形的数字,数字值越小,优先级越高。
我们再编写一个切面类aspect2,aspect和aspect2:分别如下:
除了@Order值不同外,其余都一致。
由此我们可以清楚地看到切面的执行顺序。
与全注解开发IOC时相同,同样创建一个类作为spring容器的配置文件,但是AOP要多添加一条自动代理的语句,添加了这条语句spring才能自动的帮你创建切面类:
只需要添加一条@EnableAspectJAutoProxy就可以实现自动代理,是不是很简单
这里简单的提一下,@EnableAspectJAutoProxy有一个ProxyTargetClass属性,为布尔类型,当其值为true的时候,表示使用cglib动态代理,为false时使用JDK动态代理,JDk动态代理只能代理接口,cglib动态代理既能代理接口又能代理了类,当值为false时,如果没有接口则还是会代理类,在Spring5中AOP的默认代理方式是JDK动态代理。在XML配置中自动代理的标签
直接编写Spring配置:
进行测试:
成功得到结果:
那么到这里对IOC的AOP的大致内容便结束了,这只是我个人的一篇学习笔记,如果有错误,还希望大家能够指正,我们一起进步!!