一、认识Spring
-
Spring是一个轻量级的**控制反转(IoC)和面向切面(AOP)**的容器(框架)
-
Spring 框架是一个分层架构,由 7 个定义良好的模块组成。Spring 模块构建在核心容器之上,核心容器定义了创建、配置和管理 bean 的方式
二、IOC基础
-
制反转IoC(Inversion of Control),是一种设计思想,DI(依赖注入)是实现IoC的一种方法,也有人认为DI只是IoC的另一种说法。
-
个人认为:控制反转就是由程序获得依赖对象的方式转为个人控制获得依赖对象,IOC就是将实体类的对象交给容器去创建,我们只需要把用到的对象从容器中拿出来即可
-
IoC是Spring框架的核心内容,使用多种方式完美的实现了IoC,可以使用XML配置,也可以使用注解,新版本的Spring也可以零配置实现IoC。
-
采用XML方式配置Bean的时候,Bean的定义信息是和实现分离的,而采用注解的方式可以把两者合为一体,Bean的定义信息直接以注解的形式定义在实现类中,从而达到了零配置的目的。
-
控制反转是一种通过描述(XML或注解)并通过第三方去生产或获取特定对象的方式。在Spring中实现控制反转的是IoC容器,其实现方法是依赖注入(Dependency Injection,DI)。
三、第一个Spring程序
-
先导入jar包,这里使用Maven进行导入
<dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>5.1.10.RELEASE</version> </dependency>
-
写Hello的实体类
public class Hello { private String name; public String getName() { return name; } public void setName(String name) { this.name = name; } public void show(){ System.out.println("Hello,"+ name ); } }
-
编写Spring的配置文件,这里命名为beans.xml
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <!--将实体类对象交给Spring去处理--> <!--bean就是java对象 , 由Spring创建和管理--> <bean id="hello" class="com.kuang.pojo.Hello"> <property name="name" value="Spring"/> </bean> </beans>
-
编写测试类,从Spring容器中获取实体类对象
@Test public void test(){ //解析beans.xml文件 , 生成管理相应的Bean对象 ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml"); //getBean : 参数即为spring配置文件中bean的id . Hello hello = (Hello) context.getBean("hello"); hello.show(); }
-
思考
Hello 对象是谁创建的 ? 【hello 对象是由Spring创建的 Hello 对象的属性是怎么设置的 ? hello 对象的属性是由Spring容器设置的 这个过程就叫控制反转 : 控制 : 谁来控制对象的创建 , 传统应用程序的对象是由程序本身控制创建的 , 使用Spring后 , 对象是由Spring来创建的 反转 : 程序本身不创建对象 , 而变成被动的接收对象 . 依赖注入 : 就是利用set方法来进行注入的.
-
依赖注入: IOC是一种编程思想,由主动的编程变成被动的接收
我们修改beans.xml,新增一个Spring配置文件beans.xml <?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <bean id="MysqlImpl" class="com.kuang.dao.impl.UserDaoMySqlImpl"/> <bean id="OracleImpl" class="com.kuang.dao.impl.UserDaoOracleImpl"/> <bean id="ServiceImpl" class="com.kuang.service.impl.UserServiceImpl"> <!--注意: 这里的name并不是属性 , 而是set方法后面的那部分 , 首字母小写--> <!--引用另外一个bean , 不是用value 而是用 ref--> <property name="userDao" ref="OracleImpl"/> </bean> </beans>
-
测试依赖注入
@Test public void test2(){ ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml"); UserServiceImpl serviceImpl = (UserServiceImpl) context.getBean("ServiceImpl"); serviceImpl.getUser(); }
-
要实现不同的操作 , 只需要在xml配置文件中进行修改 , 所谓的IoC,一句话搞定 : 对象由Spring 来创建 , 管理 , 装配 !
四、IOC创建对象
-
通过无参构造来创建
<bean id="user" class="com.kuang.pojo.User"> <property name="name" value="kuangshen"/> </bean>
-
通过有参构造来创建
<!-- 第一种根据index参数下标设置 --> <bean id="userT" class="com.kuang.pojo.UserT"> <!-- index指构造方法 , 下标从0开始 --> <constructor-arg index="0" value="kuangshen2"/> </bean> <!-- 第二种根据参数名字设置 --> 比较常用 <bean id="userT" class="com.kuang.pojo.UserT"> <!-- name指参数名 --> <constructor-arg name="name" value="kuangshen2"/> </bean> <!-- 第三种根据参数类型设置 --> <bean id="userT" class="com.kuang.pojo.UserT"> <constructor-arg type="java.lang.String" value="kuangshen2"/> </bean>
-
结论:在配置文件加载的时候。其中管理的对象都已经初始化了!
五、Spring的配置
-
alias 设置别名 , 为bean设置别名 , 可以设置多个别名
第一种方式 <!--设置别名:在获取Bean的时候可以使用别名获取--> <alias name="userT" alias="userNew"/> <!--alias标签中,name属性值对象bean的地址,alias中的值表示为该bean设置的别名--> 第二种方式 <bean id="hello" name="hello2 h2,h3;h4" class="com.kuang.pojo.Hello"> <property name="name" value="Spring"/> </bean>
-
团队的合作通过import来实现 :可以通过import来引入多个beans.xml配置文件
<import resource="{path}/beans.xml"/>
六、依赖注入
-
依赖 : 指Bean对象的创建依赖于容器 . Bean对象的依赖资源 .
-
注入 : 指Bean对象所依赖的资源 , 由容器来设置和装配 .
-
构造器注入:有参构造和无参构造
-
Set注入:要求被注入的属性 , 必须有set方法 , set方法的方法名由set + 属性首字母大写 , 如果属性是boolean类型 , 没有set方法 , 是 is .
-
多种注入方式
-
Address.java
public class Address { private String address; public String getAddress() { return address; } public void setAddress(String address) { this.address = address; } }
-
Student
package com.kuang.pojo; import java.util.List; import java.util.Map; import java.util.Properties; import java.util.Set; public class Student { private String name; private Address address; private String[] books; private List<String> hobbys; private Map<String,String> card; private Set<String> games; private String wife; private Properties info; public void setName(String name) { this.name = name; } public void setAddress(Address address) { this.address = address; } public void setBooks(String[] books) { this.books = books; } public void setHobbys(List<String> hobbys) { this.hobbys = hobbys; } public void setCard(Map<String, String> card) { this.card = card; } public void setGames(Set<String> games) { this.games = games; } public void setWife(String wife) { this.wife = wife; } public void setInfo(Properties info) { this.info = info; } public void show(){ System.out.println("name="+ name + ",address="+ address.getAddress() + ",books=" ); for (String book:books){ System.out.print("<<"+book+">>\t"); } System.out.println("\n爱好:"+hobbys); System.out.println("card:"+card); System.out.println("games:"+games); System.out.println("wife:"+wife); System.out.println("info:"+info); } }
-
常量注入
<bean id="student" class="com.kuang.pojo.Student"> <property name="name" value="小明"/> </bean>
-
Bean注入
<bean id="addr" class="com.kuang.pojo.Address"> <property name="address" value="重庆"/> </bean> <bean id="student" class="com.kuang.pojo.Student"> <property name="name" value="小明"/> <property name="address" ref="addr"/> </bean>
-
数组注入
<bean id="student" class="com.kuang.pojo.Student"> <property name="name" value="小明"/> <property name="address" ref="addr"/> <property name="books"> <array> <value>西游记</value> <value>红楼梦</value> <value>水浒传</value> </array> </property> </bean>
-
List注入
<property name="hobbys"> <list> <value>听歌</value> <value>看电影</value> <value>爬山</value> </list> </property>
-
Map注入
<property name="card"> <map> <entry key="中国邮政" value="456456456465456"/> <entry key="建设" value="1456682255511"/> </map> </property>
-
set注入
<property name="games"> <set> <value>LOL</value> <value>BOB</value> <value>COC</value> </set> </property>
-
Null注入
<property name="wife"><null/></property>
-
Properties注入
<property name="info"> <props> <prop key="学号">20190604</prop> <prop key="性别">男</prop> <prop key="姓名">小明</prop> </props> </property>
-
测试结果
-
-
p命名和c命名注入
1、P命名空间注入 : 需要在头文件中加入约束文件 导入约束 : xmlns:p="http://www.springframework.org/schema/p" <!--P(属性: properties)命名空间 , 属性依然要设置set方法--> <bean id="user" class="com.kuang.pojo.User" p:name="狂神" p:age="18"/> 2、c 命名空间注入 : 需要在头文件中加入约束文件 导入约束 : xmlns:c="http://www.springframework.org/schema/c" <!--C(构造: Constructor)命名空间 , 属性依然要设置set方法--> <bean id="user" class="com.kuang.pojo.User" c:name="狂神" c:age="18"/> c命名注入就是所谓的有参构造器注入,要写有参构造和无参构造
-
Bean的作用域:bean就是由IoC容器初始化、装配及管理的对象
-
几种作用域中,request、session作用域仅在基于web的应用中使用(不必关心你所采用的是什么web应用框架),只能用在基于web的Spring ApplicationContext环境。
七、自动装配
-
@Autowired
- @Autowired是按类型自动转配的,不支持id匹配。
- 需要导入 spring-aop的包!
1、将User类中的set方法去掉,使用@Autowired注解 public class User { @Autowired private Cat cat; @Autowired private Dog dog; private String str; public Cat getCat() { return cat; } public Dog getDog() { return dog; } public String getStr() { return str; } }
<context:annotation-config/> <bean id="dog" class="com.kuang.pojo.Dog"/> <bean id="cat" class="com.kuang.pojo.Cat"/> <bean id="user" class="com.kuang.pojo.User"/> <!--此时容器会将dog和cat自动装配到User实体类中-->
-
@Autowired(required=false) 说明:false,对象可以为null;true,对象必须存对象,不能为null。
//如果允许对象为null,设置required = false,默认为true @Autowired(required = false) private Cat cat;
-
@Qualifier
-
@Autowired是根据类型自动装配的,加上@Qualifier则可以根据byName的方式自动装配
-
@Qualifier不能单独使用。
-
适用于同种实体类在beans.xml中有多个配置名,加入该属性,可根据自己需要的属性名来获取对象
@Autowired @Qualifier(value = "cat2") private Cat cat; @Autowired @Qualifier(value = "dog2") private Dog dog;
-
-
@Resource
-
@Resource如有指定的name属性,先按该属性进行byName方式查找装配;
-
其次再进行默认的byName方式进行装配;
-
如果以上都不成功,则按byType的方式自动装配。
-
都不成功,则报异常。
public class User { //如果允许对象为null,设置required = false,默认为true @Resource(name = "cat2") private Cat cat; @Resource private Dog dog; private String str; } @Resource先进行byName查找,失败;再进行byType查找,成功。
-
-
结论
@Autowired与@Resource异同: 1、@Autowired与@Resource都可以用来装配bean。都可以写在字段上,或写在setter方法上。 2、@Autowired默认按类型装配(属于spring规范),默认情况下必须要求依赖对象必须存在,如果要允许null 值,可以设置它的required属性为false,如:@Autowired(required=false) ,如果我们想使用名称装配可以结合@Qualifier注解进行使用 3、@Resource(属于J2EE复返),默认按照名称进行装配,名称可以通过name属性进行指定。如果没有指定name属性,当注解写在字段上时,默认取字段名进行按照名称查找,如果注解写在setter方法上默认取属性名进行装配。当找不到与名称匹配的bean时才按照类型进行装配。但是需要注意的是,如果name属性一旦指定,就只会按照名称进行装配。 它们的作用相同都是用注解方式注入对象,但执行顺序不同。@Autowired先byType,@Resource先byName。
八、注解开发
-
使用注解开发,在Spring4以后需要引入Aop的jar包,这里直接引入Spring的工具包
<dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>5.2.5.RELEASE</version> </dependency>
-
在配置文件当中,还得要引入一个context约束
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> </beans>
-
注入Bean对象
-
在beans.xml文件中加入扫描包的标签
<!--指定注解扫描包--> <context:component-scan base-package="cn.daitools"/>
-
在指定的扫描包内的类中添加注释
@Component("user") // 相当于配置文件中 <bean id="user" class="当前注解的类"/> public class User { public String name = "呆呆"; }
-
测试
@Test public void test(){ ApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans.xml"); User user = (User) applicationContext.getBean("user"); System.out.println(user.name); }
-
-
注入属性
@Component("user") // 相当于配置文件中 <bean id="user" class="当前注解的类"/> public class User { @Value("呆呆") // 相当于配置文件中 <property name="name" value="呆呆"/> public String name; @Value("daidai") //如果提供了set方法,在set方法上添加@value("值") public void setName(String name) { this.name = name; }
-
衍生注解
- @Controller:web层
- @Service:service层
- @Repository:dao层
-
小结
XML与注解比较 XML可以适用任何场景 ,结构清晰,维护方便 注解不是自己提供的类使用不了,开发简单方便 xml与注解整合开发 :推荐最佳实践 xml管理Bean 注解完成属性注入 使用过程中, 可以不用扫描,扫描是为了类上的注解 <context:annotation-config/> 作用: 进行注解驱动注册,从而使注解生效 用于激活那些已经在spring容器里注册过的bean上面的注解,也就是显示的向Spring注册 如果不扫描包,就需要手动配置bean 如果不加注解驱动,则注入的值为null!
-
基于Java类进行配置
-
编写一个实体类Dog
@Component //将该类注解为Bean对象 public class Dog { public String name; public void show(){ System.out.println("汪汪汪"); } }
-
编写一个注解配置类
@Configuration //使用该注解,将此类设置为注解类 public class Mydog { @Bean //使用该注解将方法注册为一个Bean对象,该处的方法名就是Bean的Id属性值 public Dog Mydog() { Dog dog = new Dog(); dog.name="Mydog"; return dog; } }
-
编写测试类
ApplicationContext context = new AnnotationConfigApplicationContext(Mydog.class); Dog mydog = (Dog) context.getBean("Mydog"); mydog.show(); System.out.println(mydog.name);
-
-
导入其他配置
//先编写一个配置类 @Configuration //代表这是一个配置类 public class MyConfig2 { } //导入合并其他配置类,类似于配置文件中的 inculde 标签 @Import(MyConfig2.class)
九、代理模式
-
静态代理(中介卖房)
-
抽象角色(接口)
public interface Rent { //租房 public void Rent(); }
-
真实角色
public class Host implements Rent{ //房东 @Override public void Rent() { System.out.println("房东要出租房子!"); } }
-
代理角色
//理解:代理可以在原有的服务上增加更多的功能 public class Proxy implements Rent{ //代理 private Host host; public Proxy(){} public Proxy(Host host) { this.host = host; } public void Rent(){ home(); host.Rent(); contract(); charge(); } void charge(){ System.out.println("中介收房租"); } void home(){ System.out.println("中介带你去看房"); } void contract(){ System.out.println("中介给你签合同"); } }
-
客户端(测试类)
public class Client { public static void main(String[] args) { Host host = new Host(); Proxy proxy = new Proxy(host); proxy.Rent(); } } //静态代理模式的好处 /* 1、将真实角色的操作处理的更加纯粹,不用关注其他业务 2、公共业务交给代理角色!实现了业务的分工 3、公共业务发生扩展时方便集中管理 //缺点 一个真实角色就会产生一个代理角色,代码量增加 */
-
-
动态代理(中介卖房)
-
抽象对象(操作接口)
public interface Rent { //租房 public void Rent(); }
-
真实对象
public class Host implements Rent{ //房东 @Override public void Rent() { System.out.println("房东要出租房子!"); } }
-
动态获得代理对象的操作类
public class ProxyInvocationHandler implements InvocationHandler { //获得所要代理的对象,这里写成万用对象的模式 private Object obj; public void setObj(Object obj) { this.obj = obj; } //动态获取代理 public Object getProxy(){ return Proxy.newProxyInstance(this.getClass().getClassLoader(), //代理类的路径 obj.getClass().getInterfaces(), //获取所要代理的真实对象的接口 this); //所要返回的代理类 } //代理执行操作并返回结果 @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { //执行代理的附加操作 seeHome(); //执行真实对象的方法并返回结果 Object result = method.invoke(obj,args); fare(); return result; } //添加代理的附加操作 public void seeHome(){ System.out.println("中介带你看房子!"); } public void fare(){ System.out.println("收中介费"); } } //Object invoke(Object proxy, 方法 method, Object[] args); //参数 //proxy - 调用该方法的代理实例 //method -所述方法对应于调用代理实例上的接口方法的实例。方法对象的声明类将是该方法声明的接口,它可以是代理类继承该方法的代理接口的超级接口。 //args -包含的方法调用传递代理实例的参数值的对象的阵列,或null如果接口方法没有参数。原始类型的参数包含在适当的原始包装器类的实例中,例如java.lang.Integer或java.lang.Boolean 。
-
客户端(测试类)
public class Client { public static void main(String[] args) { //获取真实对象 Host host = new Host(); //动态获取代理类对象 ProxyInvocationHandler Pih = new ProxyInvocationHandler(); //设置被代理的真实对象 Pih.setObj(host); //动态生成代理类 Rent proxy = (Rent)Pih.getProxy(); //执行代理操作 proxy.Rent(); } }
-
-
总结:一个动态代理 , 一般代理某一类业务 , 一个动态代理可以代理多个类,代理的是接口!
十、AOP
-
AOP是可以生成动态代理的一个框架,使用AOP需要提前导入AOP的jar文件,这里使用Maven导入依赖
<dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>1.9.4</version> </dependency>
-
在beans.xml文件中加入context约束,这里使用通用的约束文件
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
-
AOP的切入点
-
创建一个UserService接口
public interface UserServiceAop { public void add(); public void delete(); public void update(); public void query(); }
-
编写UserService接口的实现类
@Component("UserServiceAopImpl") public class UserServiceAopImpl implements UserServiceAop { @Override public void add() { System.out.println("增加了一个用户"); } @Override public void delete() { System.out.println("删除了一个用户"); } @Override public void update() { System.out.println("修改了一个用户"); } @Override public void query() { System.out.println("查询了一个用户"); } }
-
写两个log类,用于实现代理操作时执行的方法
@Component("log") public class log implements MethodBeforeAdvice { @Override public void before(Method method, Object[] objects, Object o) throws Throwable { System.out.println("使用了"+method.getName()+"方法"); } } @Component("log2") public class log2 implements AfterReturningAdvice { @Override public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable { System.out.println(method.getName()+"执行完毕,并返回回一个结果:"+returnValue); } }
-
上面写出的这几个类已经通过注解的方式注册了bean,接下来就要在bean.xml中配置AOP的相关信息,这里主要设置AOP的切入点和在切入点所进行的操作
<aop:config> <!--//设置切入点--> <aop:pointcut id="piointcut" expression="execution(* Day06.Aop原生写法.UserServiceAopImpl.*(..))"/> <!--//执行环绕增加--> <aop:advisor advice-ref="log" pointcut-ref="piointcut"/> <aop:advisor advice-ref="log2" pointcut-ref="piointcut"/> </aop:config>
-
编写测试类进行测试,注意的是,这里使用AOP所生成的代理类,代理的是一个接口,所以使用getBean()获取的对象为接口对象
@Test //第六天测试Aop-切入点写法 public void beforAfter(){ ApplicationContext context = new ClassPathXmlApplicationContext("Aop.xml"); UserServiceAop aop = (UserServiceAop) context.getBean("UserServiceAopImpl"); aop.add(); }
-
-
AOP的切入面:使用AOP的切入面是自己定义一个普通类,里面可以随便写方法,然后将该类作为一个切面,类中的方法可以在切入点随意用,前面几步不变,改变的是在bean.xml中进行的配置
<!--配置Aop-切入面写法--> <aop:config> <!--diy注册为Bean方法作为Diy的对象--> <aop:aspect ref="diy"> <!--//设置主方法切入点--> <aop:pointcut id="pointcut" expression="execution(* Day06.Aop_切入面.UserAopImpl.*(..))"/> <aop:before method="before" pointcut-ref="pointcut"/> <aop:after method="after" pointcut-ref="pointcut"/> </aop:aspect> </aop:config>
-
AOP的注解写法
-
直接在作为AOP切面的类中写上注解
@Component("diyAn") @Aspect //将该类注册为切面 public class DiyAn { @Before("execution(* Day06.Aop_注解写法.UserAopAnImpl.*(..))") public void before() { System.out.println("----------方法执行前----------"); } @After("execution(* Day06.Aop_注解写法.UserAopAnImpl.*(..))") public void after() { System.out.println("----------方法执行后----------"); } }
-
在bean.xml中插入一个自动扫描的标签
<aop:aspectj-autoproxy/>
-