Spring学习记录
这是期末作业,本文内容基于个人理解和官方文档以及网上一些资料,如有错误烦请评论区指出
1. Spring是什么
- Spring是一个开源免费的框架 , 容器 .
- Spring是一个轻量级的框架 , 非侵入式的 .
- 控制反转 IoC , 面向切面 Aop
- 对事物的支持 , 对框架的支持
2. IoC
- IoC,中文名字控制反转,在Spring中是一个容器,在我们以前的学习的程序中,对使用者来说,程序的控制创建都是交给程序的,但是控制反转则是由我们自行控制创建对象,把主动权交给了调用者,例如程序需要一个对象的时候,不直接在原来的类或者方法中创建,而是由调用者传入。
- 实现IoC的方法有很多种,现在学习的Spring使用的是DI,也就是依赖注入,上文中由调用者传入一个对象指的就是依赖注入,而控制反转的指的就是获得依赖对象的方式反转了。
3. 使用Spring
和使用大多数框架一样,我们首先需要导入必要的包,如果使用maven则可以直接写入配置文件:
<dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>5.1.10.RELEASE</version> </dependency>
编写一个实体类:
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配置文件applicationContext.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就是java对象 , 由Spring创建和管理--> <bean id="hello" class="com.kuang.pojo.Hello"> <property name="name" value="Spring"/> </bean> </beans>
编写测试类:
@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(); }
在上面的例子中,对象的创建从我们写的程序变成了Spring容器,这就是控制反转。在Hello类中有set方法,从而可以进行依赖注入。
4. 依赖注入和自动装配
依赖注入
依赖注入有配置文件注入和注解注入两种,配置文件注入中有一下几种方式:
- 常量注入
- Bean注入
- 数组注入
- List注入
- Map注入
- Set注入
- Null注入
- Properties注入
- 这其中需要注意的是,在传入值的时候,分为value和ref两种,前者指的是值类型,后者是引用类型,在依赖注入的时候,被注入的对象必须要有set方法,其命名要符合JavaBean规范。
- 具体注入方式参考官网,在此不表。
自动装配
在Spring中有三种装配方式,分别是:
- 在xml中显式配置;
- 在java中显式配置;
- 隐式的bean发现机制和自动装配
在自动化的装配中,我们可以给bean标签加上autowire属性,分别有byName和byType两种,顾名思义,前者是按名字装配,后者是按类型装配。
5. 注解
Spring中的注解种类繁多,使用注解可以大幅减小配置文件的数量,降低开发难度。使用注解首先要开启注解支持,还要引入aop的包,再加上context约束:
<context:annotation-config/>
<dependency> <groupId>org.springframework</groupId> <artifactId>spring-aop</artifactId> <version>5.0.7.RELEASE</version> </dependency>
<?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>
以下是几种常见的Spring注解:
@Configuration 相当于配置文件中的beans,使用之前要在配置文件中开启扫描
<context:component-scan base-package="xxx.xxx"/>
- @Scope 可以配置作用域
- @Value 可以手动给属性注入值
@Controller, @Service, @Repository,@Component 相当于bean标签,在类上使用,使用前要在配置文件中开启扫描
<context:component-scanbase-package=”XXX”/>
以上四种注解功能相同,只是为了区分所以名字不同。使用以上四种标签时,默认id为类名首字母转小写。
- @Autowired 配置在属性上,实现自动装配
- @Bean 相当于Bean标签,在方法上使用,方法的名字就相当于bean标签中的id,其返回值相当于class属性。
6. 代理模式
在学习AOP之前,先要学习代理模式,因为AOP的底层机制就是动态代理。
代理模式分为两种,一种是动态代理,一种是静态代理。、
在静态代理中,对象有以下四个:
- 抽象角色:使用接口或者抽象类来实现
- 真实角色:被代理的角色
- 代理角色:代理真实的角色,然后做一些附加操作
- 客户:使用代理觉得来做一些事情。
综上所述,之所以要使用代理模式是因为原来的角色(真实角色),多出了需求,但是因为某些原因不适合更改真实对象的代码时,就可以使用代理对象来代替真实对象完成工作。这样一来,有如下几点好处:
- 真实对象的工作是纯粹的,不用关注细枝末节
- 可以让业务扩展时变得集中和方便管理。
- 实现了业务的分工
但是相对应的,代理类越多,工作量就越大,开发效率遍低。
- 动态代理
动态代理有以下几点:
- 动态代理的角色和静态代理的一样 .
- 动态代理的代理类是动态生成的 . 静态代理的代理类是我们提前写好的
- 动态代理分为两类 : 一类是基于接口动态代理 , 一类是基于类的动态代理
举一个例子:
//一个用户增删改查的接口 package top.acgnx.service; public interface UserService { public void add(); public void delect(); public void update(); public void query(); }
//一个实现类作为真实对象,模拟增删改查操作 package top.acgnx.service; public class UserServiceImpl implements UserService{ public void add() { System.out.println("add User"); } public void delect() { System.out.println("delect User"); } public void update() { System.out.println("update User"); } public void query() { System.out.println("query User"); } }
现在需要对上面的真实对象进行增强,使其具有输出日志的功能。
import java.lang.reflect.Proxy; //代理对象 public class ProxyInvocationHandler implements InvocationHandler { Object object; //传入要被代理的真实对象 public void setObject(Object object) { this.object = object; } //使用newProxyInstance方法创建一个代理类并返回 public Object getProxy(){ return Proxy.newProxyInstance(this.getClass().getClassLoader(),object.getClass().getInterfaces(),this); } //执行被调用的方法,可以在这里面对原有的方法进行增强 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { log(method.getName()); //执行传入的方法 Object ret = method.invoke(object, args); //返回执行的结果 return ret; } public void log(String s){ System.out.println("方法" + s + "被执行了"); } }
上面这个类实现了InvocationHandler接口,传入一个真实对象,就可以使用getProxy()方法得到一个被代理后的对象,代理对象调用真实对象中的方法的时候,就会通过invoke被执行,同时增强原有的方法,并把执行后的结果返回。
import org.junit.Test; import top.acgnx.service.ProxyInvocationHandler; import top.acgnx.service.UserService; import top.acgnx.service.UserServiceImpl; public class ProxyText { @Test public void proxyTest(){ //创建真实对象 UserServiceImpl userService = new UserServiceImpl(); //创建代理对象 ProxyInvocationHandler pih = new ProxyInvocationHandler(); //注入要代理的对象 pih.setObject(userService); //得到被代理后的对象并强转类型 UserService proxy = (UserService) pih.getProxy(); //执行增强后的方法 proxy.add(); } }
上述代码可以传入任意一个真实对象,并在执行原有方法前输出方法名。