1 Spring
1.1简介
- Spring:春天——>给软件行业带来了春天!
- 2002年,首次推出了Spring框架的雏形,interface21框架!
- Spring框架即以Interface21框架为基础 ,经过重新设计,并不断丰富起内涵,于2004年3月24日,发布了1.0正式版。
- Rob Johnson,Spring Framework创始人,著名作者,很难想象Rob Johnson的学历,真的让 好多人大吃一惊,他是悉尼大学的博士,然而他的专业不是计算机,而是音乐学 ;
- Spring理念:使现有的技术更加容易,本身是一个大杂烩 。
1.2优点
- Spring是一个免费的开源的容器;
- Spring是一个轻量级的、非入侵式的框架;
- 控制反转(IOC)、面向切面编程(AOP);
- 支持事务的处理,对框架整合的支持;
总结:Spring就是一个轻量级的控制反转(IOC)和面向切面编程(AOP)的框架。
2 IOC理论推导
-
UserDao接口
public interface UserDao { void UserList(); }
-
UserDaoImpl实现类
public void UserList() { System.out.println("获取用户所有信息"); }
-
UserSerivce业务接口
public interface UserService { void UserListService(); }
-
UserServiceImpl实现类
UserDao userDao; public void setUserDao(UserDao userDao) { this.userDao=userDao; } public void UserListService() { userDao.UserList(); } }
在我们之前的业务中,用户的需求可能会影响我们原来的代码,我们需要根据用户的需求修改源代码。如果程序代码量非常大,修改一次的成本代价非常昂贵。
我们使用一个set接口实现,已经发生了革命性的变化;
public void setUserDao(UserDao userDao)
{
this.userDao=userDao;
}
- 之前,程序是主动创建对象,控制权在程序猿手上;
- 使用了set注入后,程序不再具有主动性,而是变成了被动的接受对象;
这种思想,从本质上解决了问题,我们不用再去管理对象的创建了。系统的耦合性降低,可以专注在业务上。这是IOC的原型。
2.1IOC本质
控制反转IOC(Inversion of control)是一种设计思想,DI(依赖注入)是实现IOC的一种方法,也有人认为DI只是IOC的另一种说法。没有IOC的程序中,我们使用面向对象编程,对象的创建与对象间的依赖关系完全硬编码在程序中,对象的创建由程序自己控制,控制反转后将对象的创建转移给第三方,个人认为所谓控制反转就是:获得 依赖对象的方式反转了。
采用XML方式配置Bean的时候,Bean的定义信息是和实现分离的,而采用注解的方式可以把两者合为一体,Bean的定义信息直接以注解的形式定义在实现类中,从而达到了零配置的目的。
控制反转是一种通过描述(XML或注释)并通过第三方去生产或获取特定对象的方式。在Spring中实现控制反转的是IOC容器,其实现方法是依赖注入(Dependency injection,DI)。
3HelloSpring
所谓的IOC,一句话搞定,就是:对象由Spring来创建、管理、装配。
思考问题?
-
Hello对象是谁创建的?
Hello对象是由Spring创建的
-
Hello对象的属性 是怎么设置的?
Hello对象的属性是由Spring容器设置的
这个过程就叫做控制反转:
控制:谁来控制对象的创建,传统应用程序的对象是由程序本身控制的,使用Spring后,对象是由Spring来 创建的。
反转:程序本身不创建对象,而编程被动的接收对象
依赖注入:就是利用set方式来进行注入的
IOC是一种编程思想,由主动的编程变成被动的接收
4IOC创建对象的方式
-
使用无参构造创建对象,默认!
-
假设我们要使用有参构造创建对象;
-
下标赋值:
<bean id="User" class="com.pledge.pojo.User"> <constructor-arg index="0" value="pledge"/> </bean>
-
通过类型创建对象
<bean id="User" class="com.pledge.pojo.User"> <constructor-arg type="java.lang.String" value="wufenfen"/> </bean>
-
直接通过参数名创建对象
<bean id="User" class="com.pledge.pojo.User"> <constructor-arg type="java.lang.String" value="wufenfen"/> </bean>
总结:在配置文件加载的时候,容器中管理的对象就已经初始化了!
-
5Spring配置
5.1别名
<alias name="User" alias="UserNew"/>
5.2Bean配置
<!-- id:bean的唯一标识符,也就是相当于我们学的对象名
class:bean对象所对应的全限定名:包名+类名
name:也是别名 ,而用Name可以取多个别名-->
<bean id="user" class="com.pledge.pojo.User" name="user2,u2">
<property name="name" value="plegde"/>
</bean>
5.3import
这个import,一般用于团队开发使用,可以将多个配置文件,导入合并为一个。
6依赖注入
6.1构造器注入
6.2set方法注入
- 依赖注入:set注入
- 依赖:bean对象 的创建依赖容器
- 注入:bean对象中的所有属性,由容器来注入
6.3拓展方式注入
【环境搭建】
-
复杂类型
public class Address { rivate String address; public String getAddress() { return address; } public void setAddress(String address) { this.address = address; }}
-
真实测试对象
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 Properties info; private String wife;
-
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 https://www.springframework.org/schema/beans/spring-beans.xsd"> <!-- 第一种,普通值注入,value--> <bean id="student" class="com.pledge.pojo.Student"> <property name="name" value="鲍玉静"/> </bean> </beans>
-
测试类
public class MyTest { public static void main(String[] args) { ApplicationContext context= new ClassPathXmlApplicationContext("beans.xml"); Student student=(Student)context.getBean("student"); System.out.println(student.getName()); } }
完善注入信息
<bean id="address" class="com.pledge.pojo.Address"> <property name="address" value="address"/> </bean> <bean id="student" class="com.pledge.pojo.Student"> <property name="name" value="鲍玉静"/> <property name="address" ref="address"/> <property name="books"> <array> <value>《西游记》</value> <value>《红楼梦》</value> <value>《水浒传》</value> </array> </property> <property name="hobbys"> <list> <value>睡觉</value> <value>玩游戏</value> <value>学习</value> </list> </property> <property name="card"> <map> <entry key="身份证" value="132746324"/> <entry key="银行卡" value="8467573285675647"/> </map> </property> <property name="games"> <set> <value>LOL</value> <value>BOB</value> <value>COC</value> </set> </property> <property name="wife"> <null /> </property> <property name="info"> <props> <prop key="学号">137264</prop> <prop key="性别">女</prop> </props> </property> </bean>
7 Bean的自动装配
- 自动装配是Spring满足bean依赖的一种方式!
- Spring会在上下文中自动寻找,并自动给Bean装配属性!
在Spring中有三种装配的方式
- 在xml中显示的配置;
- 在java中显示配置
- 隐式的自动装配bean【重要】
7.1测试
环境搭建:一个人有两个宠物!
7.2ByName自动装配
<bean id="people" class="com.pledge.pojo.People" autowire="byName">
<property name="name" value="pledge"/>
</bean>
7.3ByType自动装配
<bean id="people" class="com.pledge.pojo.People" autowire="byType">
<property name="name" value="pledge"/>
</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
直接在属性上使用即可,也可以在set方式上使用!(可以忽略set方法使用)
使用Autowired我们可以不用编写set方法了,前提是你这个自动装配的属性在IOC(Spring)容器中存在,且符合名字ByName。
科普:
@Nullable 字段标记了这个注解,说明这个字段可以为null
public @interface Autowired{ boolean required() default true;}
//如果显示定义了Autowired的required属性为false,说明这个对象可以为null;否则不允许为空 @Autowired(required = false) private Dog dog; @Autowired private Cat cat; private String name;
如果@Autowired自动装配的环境比较复杂,自动装配无法通过一个注解【Autowired】完成的时候,我们可以使用@Qualifier(value=“xxx”)去配置@Autowired的使用,指定一个唯一的bean对象注入!
public class People { //如果显示定义了Autowired的required属性为false,说明这个对象可以为null;否则不允许为空 @Autowired private Dog dog; @Autowired @Qualifier(value = "cat1") private Cat cat; private String name;
@Resource注解
public class People { //如果显示定义了Autowired的required属性为false,说明这个对象可以为null;否则不允许为空 @Resource(name="dog1") private Dog dog; @Resource private Cat cat; private String name;
小结:
@Resource和@Autowired的区别:
- 都是用来自动装配的,都可以放在属性字段上;
- @Autowired通过ByType的方式实现,而且必须要求这个对象存在;
- @Resource默认通过ByName的方式实现,如果找不到名字,则通过ByType实现;如果两个都找不到的情况,就报错!
- 执行顺序不同:@Autowired通过Type的方式实现,@Resource默认通过ByType的方式实现。
8使用注解开发
在Spring4之后,要使用注解开发,必须要保证aop的包导入了
使用注解需要导入context约束,增加注解的支持!
-
bean
-
属性如何注入
@Componentpublic class User { @Value("吴芬芬") public String name;}
-
衍生的注解
@Component有几个衍生注解,我们在web开发中,会按照MVC三层架构分层!
-
dao【@Repository】
-
controller【@Controller】
-
service【@Service】
这四个注解功能都是一样的,都是代表将某个类注册到容器中,装配Bean
-
-
自动装配
-
作用域
-
小结
xml与注解:
- xml更加万能,适用于任何场合,维护简单方便;
- 注解不是自己的类使用不了,维护相对复杂;
xml与注解最佳实践:
- xml用来管理bean;
- 注解只负责完成属性的注入;
- 我们在使用的过程中,只需要注意一个问题,必须让注解生效,就需要开启注解的支持
<!--指定需要扫描的包,这个包下的注解都会生效--> <context:component-scan base-package="com.pledge"/> <context:annotation-config/>
9 使用Java的方式配置Spring
我们现在要完全不使用Spring的xml配置了,全权交给Java来做!
JavaConfig是Spring的一个子项目,在spring4之后,它成为了一个核心功能。
实体类
@Component public class User { @Value("吴芬芬") //属性 private String name; public String getName() { return name; } public void setName(String name) { this.name = name; } @Override public String toString() { return "User{" + "name='" + name + '\'' + '}'; } }
@Configuration//这个也会被Spring容器托管,注册到容器中,因为它本来就是一个@Component //@Configuration代表这是一个配置类,就和我们之前看到的beans.xml是一样的 @ComponentScan("com.pledge.pojo") @Import(PledgeConfig.class) public class PledgeConfig { //注册一个bean,就相当于我们之前写的一个Bean标签,id是这个方法的名字 //就相当于bean标签中的id属性 //这个方法的返回值,就相当于bean标签中的class属性 @Bean public User getUser(){ return new User(); //就是返回要注入到bean的对象 } }
public class MyTest { public static void main(String[] args) { //如果完全使用了配置类方式去做,我们就只能通过annotationConfig上下文来获取容器 ApplicationContext context = new AnnotationConfigApplicationContext(PledgeConfig.class); User user=(User)context.getBean("getUser"); System.out.println(user.getName()); } }
这种纯java的配置方式,在SpringBoot中随处可见!
9代理模式
为什么要学习代理模式?因为这就是SpringAOP的底层【SpringAOP和SpringMVC】
代理模式的分类:
- 静态代理
- 动态代理
9.1静态代理
角色分析:
- 抽象角色:一般会使用接口或抽象类来解决
- 真实角色:被代理的角色
- 代理角色:代理真实角色、代理真实角色后,一般会做一些附属操作。
- 客户:访问代理对象的人
代码步骤:
接口
//租房public interface Rent { public void rent();}
真实角色
//房东public class Host implements Rent{ 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(){
seeHouse();
host.rent();
hetong();
fare();
}
public void seeHouse(){
System.out.println("中介带你看方");
}
public void hetong(){
System.out.println("签租赁合同");
}
public void fare(){
System.out.println("中介收中介费");
}
}
客户端访问代理角色
public class Client {
public static void main(String[] args) {
//房东要租房子
Host host=new Host();
//代理,中介帮房东租房子,代理角色一般会有附属操作
Proxy proxy = new Proxy(host);
//你不用面对房东,直接找中介租房即可!
proxy.rent();
}
}
代理模式的好处:
- 可以使真实角色的操作更加纯粹!不用去关注一些公共的业务!
- 公共也就交给代理角色!实现了业务的分工!
- 公共业务发生扩展的时候,方便集中管理!
缺点:
- 一个真实角色就会产生一个代理角色,代码量会饭呗,开发效率会变低!
9.2动态代理
- 动态代理和静态代理角色一样
- 动态代理的代理类是动态生成的,不是我们直接写好的
- 动态代理分为两大类:基于接口的动态代理,基于类的动态代理
- 基于接口----JDK动态代理
- 基于类 :cglib
- java字节码实现:javasist
需要了解两个类:Proxy(代理),InvocationHandler(调用处理程序)
动态代理的好处:
- 可以使真实角色的操作更加纯粹!不用去关注一些公共的业务!
- 公共也就交给代理角色!实现了业务的分工!
- 公共业务发生扩展的时候,方便集中管理!
- 一个动态代理类代理的是一个接口,一般就是对应的一类业务;
- 一个动态代理类可以代理多个类,只要是实现了同一个接口即可;
10AOP
10.1什么是AOP
AOP(Aspect Oriented Programming)意为:面向切面编程,通过预编译方式和运行期动态代理 实现程序功能的统一维护的一种技术,AOP是OOP的延续,是软件开发中的一个热点,也是spring框架中的一个重要内容,是函数式编程的一种衍生泛型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑个部分之间的耦合度很低,提高程序的可重用性,同时提高了开发的效率。
10.2AOP在Spring中的作用
提供声明式事务,允许用户自定义切面
- 横切关注点:跨越应用程序多个模块的方法和功能。即是,与我们业务逻辑无关的,但是我们需要关注的部分,就是横切关注点。如日志、安全、缓存、事务等等…
- 切面(ASPECT):横切关注点 被模块化的特殊对象,即它是一个类。
- 通知(ACTIVE):切面必须要完成的工作。即,它是类中的一个方法。
- 目标(Target):被通知对象
- 代理(Proxy):向目标对象应用通知之后创建的对象;
- 切入点(PointCut):切面通知 执行的“地点”的定义
- 连接点(JoinPoint):与切入点匹配的执行点