1.1简介
- Spring:春天----->给软件行业带来了春天
- Spring是一个轻量级控制反转(ioC)和面向切面(AOP)的容器框架
- UserDao接口
- UserDaoImpl实现类
- UserService业务接口
- UserServiceImp业务类
在我们之前的业务中,用户的需求可能会影响我们原来的代码,我们需要根据用户的需求取修改代码!如果程序代码量十分大,修改一个的成本代价十分昂贵!
我们使用一个Set接口实现
private UserDao userDao;
//利用set进行动态实现值的注入
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
- 之前,程序是主动创建对象!控制权在程序员手上
- 使用了set诸如后,程序不在具有主动性,而是变成了被动的接受对象
这种思想从本质上解决了问题,我们程序员不用在管理程序对象的创建了。系统的耦合性大大降低,可以更加专注的在业务的实现上。这是IOC的原型
控制反转是一种通过描述(XML或注解)并通过第三方去生产或获取特定对象的方式。在Spring中实现控制反转的是IOC容器,其实现方法是依赖注入(Denpendency Injection,DI)
3.HelloSpring 4.IOC创建对象的方式-
使用无参构造方法,默认!
<bean id="user" class="com.sheng.pojo.User"> <property name="name" value="123"></property> </bean>
-
假设我们要使用有参构造创建对象。
-
下标值
<bean id="user" class="com.sheng.pojo.User">--> <constructor-arg index="0" value="123"> </constructor-arg>
-
类型
<bean id="user" class="com.sheng.pojo.User"> <constructor-arg type="com.sheng.pojo.User" value="123"> </constructor-arg> </bean>
-
参数名
<!--第三种有参构造:直接通过参数名来赋值--> <bean id="user" class="com.sheng.pojo.User"> <constructor-arg name="name" value="123"> </constructor-arg> </bean>
总结:在配置文件加载的时候,容器中管理的对象就已经初始化了。
-
5.1 别名
<!--别名,如果添加了别名,我们也可以使用别名获取到这个对象-->
<alias name="user" alias="adfadjfa" />
5.2 Bean的配置
<!--
id:beand的唯一标识符,也就是相当于我们学的对象名
class:bean 对象所对应的全限定名 : 包名+ 类型
name : 也是别名,而且name可以取多个别名
-->
<bean id="userT" class="com.sheng.pojo.UserT" name="user2,u2 u3;u4">
<property name="name" value="13" />
</bean>
5.3 import
这个import,一般用于团队开发使用,他可以将多个配置文件,导入合并为一个
假如,现在项目中有多个人开发,这三个人复制不同的类开发,不同的类需要注册在不同的bean中,我们可以利用import将所有人的beans.xml合并为一个总的!
-
张三
-
李四
-
王五
-
applicationContext
<import resource="beans.xml" /> <import resource="beans2.xml" /> <import resource="beans3.xml" />
使用的时候,直接使用总的配置就可以了
6.1、构造器注入
6.2、Set方式注入(重点)
-
依赖注入:Set注入!
-
依赖:bean对象的创建依赖于容器
-
注入:bean对象中的所有属性,由容器注入
【环境搭建】
-
复杂类型
public class Address { private String address; public String getAddress() { return address; } public void setAddress(String address) { this.address = address; }}
-
真实测试对象
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 String getName() { return name; } public void setName(String name) { this.name = name; } public Address getAddress() { return address; } public void setAddress(Address address) { this.address = address; } public String getBooks() { return books; } public void setBooks(String books) { this.books = books; } public List<String> getHobbys() { return hobbys; } public void setHobbys(List<String> hobbys) { this.hobbys = hobbys; } public Map<String, String> getCard() { return card; } public void setCard(Map<String, String> card) { this.card = card; } public Set<String> getGames() { return games; } public void setGames(Set<String> games) { this.games = games; } public String getWife() { return wife; } public void setWife(String wife) { this.wife = wife; } public Properties getInfo() { return info; } public void setInfo(Properties info) { this.info = info; } @Override public String toString() { return "Student{" + "name='" + name + '\'' + ", address=" + address + ", books='" + books + '\'' + ", hobbys=" + hobbys + ", card=" + card + ", games=" + games + ", wife='" + wife + '\'' + ", info=" + info + '}'; }}
-
-
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 https://www.springframework.org/schema/beans/spring-beans.xsd"> <bean id="student" class="com.sheng.pojo.Student"> <!--第一种,普通至注入,value--> <property name="name" value="123"/> </bean></beans>
-
测试类
import com.sheng.pojo.Student;import org.springframework.context.ApplicationContext;import org.springframework.context.support.ClassPathXmlApplicationContext;public class MyTest { public static void main(String[] args) { ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml"); Student student = (Student) context.getBean("student"); System.out.println(student.getName()); }}
完善注册信息
<bean id="address" class="com.sheng.pojo.Address" /> <bean id="student" class="com.sheng.pojo.Student"> <!--第一种,普通至注入,value--> <property name="name" value="123"/> <!--第二种,Bean注入,ref--> <property name="address" ref="address" /> <!--第三种,数组注入,ref--> <property name="books"> <array> <value>水浒传</value> <value>三国演义</value> <value>红楼梦</value> <value>西游记</value> </array> </property> <!--list注入--> <property name="hobbys"> <list> <value>听歌</value> <value>敲代码</value> <value>看电影</value> </list> </property> <!--Map--> <property name="card"> <map> <entry key="身份证" value="123123123123123123"/> <entry key="银行卡" value="1231231231231231231"/> </map> </property> <!--Set--> <property name="games"> <set> <value>LOL</value> <value>COC</value> <value>BOB</value> </set> </property> <!--NULL--> <property name="wife"> <null /> </property> <!--Properties--> <property name="info"> <props> <prop key="driver">123123</prop> <prop key="url">www.baidu.com</prop> <prop key="username">root</prop> <prop key="password">123456</prop> </props> </property> </bean>
6.3、 拓展方式注入
我们可以使用P命名空间和C命名空间进行注入
官方解释:
<?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:p="http://www.springframework.org/schema/p" xmlns:c="http://www.springframework.org/schema/c" xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd"> <!--P命名空间注入,可以直接注入属性的值:property--> <bean id="user" class="com.sheng.pojo.User" p:name="sheng" p:age="21" /> <!--c命名空间注入,通过构造器注入:construct-args--> <bean id="user2" class="com.sheng.pojo.User" c:age="18" c:name="xinfan" /></beans>
测试:
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("beans.xml"); User user = context.getBean("user2", User.class); System.out.println(user); }
注意点:p命名和c命名空间不能直接使用,需要导入xml约束
xmlns:p="http://www.springframework.org/schema/p" xmlns:c="http://www.springframework.org/schema/c"
6.4 Bean Scopes(作用域)
-
Singleton Scope单例模式(Spring默认机制)
<bean id="user2" class="com.sheng.pojo.User" c:age="18" c:name="xinfan" scope="singleton"/>
-
(Prototy Scope)原型模式:每次从容器中get的时候,都会产生一个新的对象
<bean id="user2" class="com.sheng.pojo.User" c:age="18" c:name="xinfan" scope="prototype"/>
-
其余的request、session、application、这些个只能在web开发中使用到
- 自动装配式Spring满足bean依赖的一种方式!
- Spring会在上下文中自动寻找,并自动给bean装备属性
在Spring中有三种装配的方式
- 在xml中显示的配置
- 在java中显示配置
- 隐式的自动装配bean 【重要】
7.1 测试
环境搭配:一个人有两个宠物!
7.2 ByName自动装配
<!-- byName:会自动在容器上下文查找,和自己对象set方法后面的值对应的beanid! --> <bean id="people" class="com.sheng.pojo.People" autowire="byName"> <property name="name" value="xiaofan" /> <!--<property name="dog" ref="dog" />--> <!--<property name="cat" ref="cat" />--> </bean>
7.3ByType自动装配
<bean class="com.sheng.pojo.Cat" /><bean class="com.sheng.pojo.Dog" /> <!-- byType:会自动在容器上下文查找,和自己对象属性类型相同的bean! --> <bean id="people" class="com.sheng.pojo.People" autowire="byType"> <property name="name" value="xiaofan" /> </bean>
小结:
- byname的时候,需要保证有所bean的id唯一,并且这个bean需要和自动注入的属性的set方法值一致
- bytype的时候,需要保证所有bean的class唯一,并且这个bean需要和自动注入的属性的类型一致
7.4 使用注解实现自动装配
jdk1.5支持的注解,spring2.5支持的注解
要使用注解须知:
-
导入约束:context约束
-
==配置注解的支持:context:annotation-config/ [重要!]
<?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 https://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd"> <context:annotation-config/></beans>
@Autowired
直接在属性上使用即可
使用Autowired我们可以不用编写set方法了,前提是你这个自动装配的属性在IOC(Spring)容器中存在,且符合名字byname!
科普:
@Nullable 字段标记了这个注解,说明这个字段可以为null;
public @interface Autowired { boolean required() default true;}
测试代码
public class People { //如果显示定义了Autowired的required属性为false,说明这个对象可以为null,否则不允许为空 @Autowired(required = false) private Cat cat; @Autowired private Dog dog; private String name;}
如果@Autowired自动装配的环境比较复杂,自动装配无法通过一个注解【@Autowired】完成的时候、我们可以使用@Qualifier(value="xxx")去配置@Autowired的使用,指定一个唯一的bean对象注入!
public class People { @Qualifier(value ="cat111") @Autowired private Cat cat; @Autowired @Qualifier(value ="dog222") private Dog dog; private String name;}
@Resource注解
public class People { @Resource(name = "cat2") private Cat cat; @Resource(name = "dog2") private Dog dog; private String name;}
小结:
@Resouce和@Autowired的区别:
- 都是用来自动装配的,都可以放在属性字段上
- @Autowired通过bytype的方式实现【常用】
- @Resouce默认通过byname的方式实现,如果找不到名字,则通过bytype实现!如果两个都找不到的情况下,就报错!【常用】
- 执行顺序不同:@Autowired通过bytype的方式实现。@Resouce默认通过byname的方式实现。
在spring4之后,使用注解必须保证AOP的包导入
使用注解需要注入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 https://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd"></beans>
@Component : 组件,放在类上,说明这个类被Spring管理了
-
bean
-
属性如何注入
@Value :
public class User { public String name; //相当于<property name = "name" value = "xiaofange" /> @Value("xiaofange") public void setName(String name) { this.name = name; }}
-
衍生的注解
@Component有几个衍生注解,我们在web开发中,会按照mvc三层架构分层!
-
dao【@Repository】
-
service【@Service】
-
controller【@Controller】
这四个注解功能都是一样的,代表将某个类注册到Spring中,装配Bean,只是为了在不同层之间区别才会用不一样的命名。
-
-
自动装配设置
@Autowired:自动装配通过类型,名字,如果Autowired不能唯一自动装配上属性,则需要通过@Qualifer(“value=xxx”)
@Resource:自动装配通过名字。类型
@Nullable:自动装配通过名字。类型
-
作用域
@Scope:
Component@Scope("singleton")public class User { public String name; //相当于<property name = "name" value = "xiaofange" /> @Value("xiaofange") public void setName(String name) { this.name = name; }}
-
小结
xml与注解:
- xml更加万能,适用于任何场合!维护简单方便
- 注解 只能自己类使用,维护相对复杂
xml与注解最佳实践:
- xml用来管理Bean;
- 注解只负责完成属性的注入;
- 我们在使用的过程中,只需要注意一个问题,必须让注解生效,就需要开启注解的支持;
我们现在要完全不适用Spring的xml配置了,全权交给java来做!
JavaConfig是Spring的一个子项目,在Spring4之后,它成为了一个核心功能
配置文件:
package com.sheng.controller;import com.sheng.pojo.User;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.ComponentScan;import org.springframework.context.annotation.Configuration;import org.springframework.context.annotation.Import;import org.springframework.stereotype.Controller;//这个也会被Spring容器托管,注册到容器中,因为它本来就是一个@Component//@Configuration代表这是一个配置类,就和我们之前看到的beans.xml一样@ComponentScan("com.sheng.pojo")@Configuration@Import(ShengConfig2.class)public class ShengConfig { //注册一个Bean,就相当于我们之前写的一个bean标签 //这个方法的名字,就相当于bean中的id属性 //这个方法的属性值,就相当于bean标签的class的属性 @Bean public User getUser(){ return new User();//就是返回要注入得到bean的对象! }}
实体类:
package com.sheng.pojo;import org.springframework.beans.factory.annotation.Value;import org.springframework.stereotype.Controller;//这个注解的意思就是说明这个类被Spring接管了,注册到了容器中@Controllerpublic class User { private String name; public String getName() { return name; } //注入值 @Value("xiaofange") public void setName(String name) { this.name = name; } @Override public String toString() { return "User{" + "name='" + name + '\'' + '}'; }}
测试文件:
public class MyTest { public static void main(String[] args) { //如果完全使用了配置类方式去做,我们就只能通过AnnotationConfig上下文来获取容器,通过配置类的class对象加载! ApplicationContext context = new AnnotationConfigApplicationContext(ShengConfig.class); User getUser = context.getBean("getUser", User.class);//取的是方法名 System.out.println(getUser.getName()); }}
这种纯Java的配置方式,在SpringBoot中随处可见!
10、代理模式为什么要学习代理模式? 因为这就是SpringAOP的底层!【SpringAOP和SpringMVC】
代理模式的分类:
- 静态代理
- 动态代理
10.1 静态代理
角色分析:
- 抽象角色:一般会使用接口或者抽象类来解决
- 真实角色:被代理的角色
- 代理角色:代理真实角色,代理真实角色后,我们一般会做一些附属操作
- 客户:访问代理对象的人
代码步骤:
-
接口
package com.sheng.demo01;public interface Rent { public void rent();}
-
真实角色
package com.sheng.demo01;//房东public class Host implements Rent{ public void rent() { System.out.println("房东要出租房子了!"); }}
-
代理角色
package com.sheng.demo01;public class Proxy implements Rent{ private Host host; public Proxy(){ } public Proxy(Host host) { this.host = host; } public void rent() { host.rent(); } //看房 public void seeHouse(){ System.out.println("中介带你看房"); } //收中介费 public void fare(){ System.out.println("收中介费"); } //收中介费 public void hetong(){ System.out.println("签租赁合同"); }}
-
客户端访问代理角色
package com.sheng.demo01;public class Client { public static void main(String[] args) { //房东要租房子 Host host = new Host(); //代理,中介帮房东租房子,但是呢?代理一般会有一些附属操作! Proxy proxy = new Proxy(host); //你不用面对房东,直接找中介即可! proxy.rent(); }}
代理模式的好处:
- 可以使真实角色的操作更加纯粹!不用去关注一些公共的业务
- 公共也就交给代理角色!实现了业务的分工!
- 公共业务发生扩展的时候,方便集中管理!
缺点:
- 一个真实角色就会产生一个代理角色;代码量会翻倍,开发效率会变低
10.2 加深理解
-
接口:
package com.sheng.demo02;public interface UserService { public void add(); public void delete(); public void update(); public void query();}
-
真实操作:
package com.sheng.demo02;public class UserServiceImpl implements UserService{ public void add() { System.out.println("增加了一个用户"); } public void delete() { System.out.println("使用了add方法"); System.out.println("删除了一个用户"); } public void update() { System.out.println("修改了一个用户"); } public void query() { System.out.println("查询了一个用户"); } //改动原有的业务代码是大忌}
-
代理操作
package com.sheng.demo02;public class UserServiceProxy implements UserService { private UserServiceImpl userService; public void setUserService(UserServiceImpl userService) { this.userService = userService; } public void add() { log("add"); userService.add(); } public void delete() { log("delete"); userService.add(); } public void update() { log("update"); userService.add(); } public void query() { log("query"); userService.add(); } //日志方法 public void log(String msg){ System.out.println("使用了"+msg+"方法"); }}
-
客户端访问代理
package com.sheng.demo02;public class Client { public static void main(String[] args) { UserServiceImpl userService = new UserServiceImpl(); UserServiceProxy proxy = new UserServiceProxy(); proxy.setUserService(userService); proxy.add(); }}
聊聊AOP
10.3 动态代理
- 动态代理和静态代理角色一样
- 动态代理的代理类是动态生成的,不是我们直接写好的!
- 动态代理分为两大类:基于接口的动态代理和基于类的动态代理
- 基于接口:JDK动态代理【我们在这里使用】
- 基于类:cglib
- java字节码实现:javasist
需要了解两个类:Proxy:代理,InvocationHander:调用处理程序
package com.sheng.demo04;import com.sheng.demo03.Rent;import java.lang.reflect.InvocationHandler;import java.lang.reflect.Method;import java.lang.reflect.Proxy;//我们会用这个类,自动生成代理类!public class ProxyInvocationHandler implements InvocationHandler{ //被代理的接口 private Object target; public void setTarget(Object target) { this.target = target; } public Object getProxy(){ return Proxy.newProxyInstance(this.getClass().getClassLoader(),target.getClass().getInterfaces(),this); } //处理代理实力,并返回结果 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { log(method.getName()); //动态代理的本质,就是使用反射机制实现! Object result = method.invoke(target, args); return result; } public void log(String msg){ System.out.println("执行了"+msg+"方法"); }}
package com.sheng.demo04;import com.sheng.demo02.UserService;import com.sheng.demo02.UserServiceImpl;public class Client { public static void main(String[] args) { //真实角色 UserServiceImpl userService = new UserServiceImpl(); //代理角色,不存在 ProxyInvocationHandler pih = new ProxyInvocationHandler(); pih.setTarget(userService);//设置要代理的对象 //动态生成代理类 UserService proxy = (UserService) pih.getProxy(); proxy.query(); }}
动态代理的好处:
- 可以使真实角色的操作更加纯粹!不用去关注一些公共的业务
- 公共也就交给代理角色!实现了业务的分工!
- 公共业务发生扩展的时候,方便集中管理!
- 一个动态代理类代理的是一个接口,一般就是对应的一类业务
- 一个动态代理类可以代理多个类,只要是实现了同一个接口即可