一、Spring介绍
Spring 是位于业务逻辑层的框架。 优点很多(无缝对接前后层的框架、提供AOP的支持 , 和以前的 Sstruts 、 Hibernate 组合成了一套框架组合 SSH 。现在是和Spring MVC 、 MyBatis 成了新的组合 SSM) 。 spring里面包含两个核心 : IOC + AOP
- IOC
IOC 的全称是 Inversion Of Control 翻译过来是控制反转的意思。 通俗的说: 就是把对象的创建工作交给spring来完成。
以前创建对象 : new 类(); ----> 把工作交给spring ----> 问spring要对象即可
- AOP
AOP为Aspect Oriented Programming的缩写,意为:面向切面编程
二、IOC演变
创建对象的发展历史
- 早期直接new对象
- 演变成工厂
- spring ioc托管
三、Spring入门
1. 入门案例
- 添加依赖
compile 'org.springframework:spring-context:4.3.17.RELEASE'
- 编写业务逻辑类
public interface UserService {
void save();
}
public class UserServiceImpl implements UserService {
@Override
public void save() {
System.out.println("调用UserServiceImpl的save方法~~");
}
}
- 创建配置文件 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 id="userService" class="com.itheima.service.impl.UserServiceImpl"/>
</beans>
- 在代码里面通过工厂获取对象
public class MainTest {
@Test
public void testSave(){
ClassPathXmlApplicationContext context = new
ClassPathXmlApplicationContext("applicationContext.xml");
UserService userService = (UserService) context.getBean("userService");
userService.save();
context.close();
}
}
2. 配置详解
1. xml解释
<!-- bean标签用来托管具体类,也就是告诉spring工厂,我们让它帮忙创建谁的实例对象
id | name: 唯一标识符,不建议写一样的内容
class : 全路径
默认创建的实例是单例.
如果想要做成多例,那么请使用scope属性,里面给值prototype ,
默认是单例 ==== singleton
-->
<bean id="us" class="com.itheima.service.impl.UserServiceImpl" scope="prototype"></bean>
2. 代码解释
//2. 让spring创建对象 问spring的工厂要对象
//创建工厂,然后告诉工厂,配置文件在哪里。 工厂就会解析这个xml文件,进而得出us --- UserServiceImpl的对应关系
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
//问工厂要对象
UserService userService =(UserService) context.getBean("us");
userService.save();
//关闭工厂,一般不会调用这个方法。
((AbstractApplicationContext) context).close();
四、IOC
此处使用两种方式给大家介绍,
xml
和注解
方式
1. xml方式
- 代码:
public class UserServiceImpl implements UserService {
@Override
public void save() {
System.out.println("调用UserServiceImpl的save方法~~");
}
}
- xml配置
<bean id="us" class="com.itheima.service.impl.UserServiceImpl"></bean>
2. 注解方式
即便使用注解,也还是需要在配置文件里面声明注解的开关
- 代码
@Compoment
public class UserServiceImpl implements UserService {
@Override
public void save() {
System.out.println("调用UserServiceImpl的save方法~~");
}
}
- 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"
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">
<!-- 打开扫描开关 -->
<context:component-scan base-package="com.itheima"/>
</beans>
- 细节
- 为了迎合三层结构的趋势,Spring 为每一个层级制定了各自的注解,
controller层使用 @Controller
service层使用@Service
dao层使用@Repository
-
如果需要给指定的bean添加标识,可以使用注解属性追加 如: @service("userService")
-
默认情况下生成的实例还是单例,如果需要做成多例,需要额外加上 @Scope("prototype")
五、依赖注入
DI : 全称是 dependency Injection 翻译过来是依赖注入 本意是在实例化对象的时候,对它里面的成员属性进行值的注入。
public class UserServiceImpl{
private String address;
}
对这个address进行赋值: 有几种方式: 两种
有参构造
set方法
依赖注入,走的就是上面的这两种。 如果使用注解注入,使用的是反射。
1. xml方式
xml方式需要在配置文件中配置,以完成值的注入,提供两种方法, 有参构造和set方法。
a. 有参构造
- 代码
public class UserServiceImpl implements UserService {
private String address;
//必须走有参构造
public UserServiceImpl(String address) {
this.address = address;
}
@Override
public void save() {
System.out.println("调用UserServiceImpl的save方法~~" + address);
}
}
- xml配置
<bean id="us" class="com.itheima.service.impl.UserServiceImpl" >
<!--
constructor-arg : 一旦配置,就指定了spring的工厂走的是 有参构造。
name:说的是属性名称
value: 注入的值 -->
<constructor-arg name="address" value="深圳"></constructor-arg>
</bean>
b. set方法
- 代码
public class UserServiceImpl implements UserService {
private String address;
public void setAddress(String address) {
this.address = address;
}
@Override
public void save() {
System.out.println("调用UserServiceImpl的save方法~~" + address);
}
}
- xml配置
<bean id="us" class="com.itheima.service.impl.UserServiceImpl" >
<!-- property标签对应的是代码里面set方法
name: 属性的名称
value: 要注入的值 -->
<property name="address" value="深圳"></property>
</bean>
c. 注入集合类型
1. 数组
代码:
private String [] address;
public void setAddress(String [] address) {
this.address = address;
}
xml:
<property name="address">
<array>
<value>北京1</value>
<value>北京2</value>
<value>北京3</value>
<value>北京天安门,我爱北京</value>
</array>
</property>
2. list
代码:
private List address;
public void setAddress(List address) {
this.address = address;
}
xml:
<property name="address">
<list>
<value>北京11</value>
<value>北京22</value>
<value>北京33</value>
<value>北京天安门,我爱北京44</value>
</list>
</property>
3. map
代码:
private Map<String , Object> address;
public void setAddress(Map<String , Object> address) {
this.address = address;
}
xml:
<!-- map集合 -->
<property name="address">
<map>
<entry key="地址1" value="北京1"/>
<entry key="地址2" value="北京2"/>
<entry key="地址3" value="北京3"/>
<entry key="地址4" value="北京4"/>
</map>
</property>
d. 注入 对象类型
在某一个类当中,持有另一个类的引用,我们需要让spring创建这个类的引用,进而通过调用set方法来完成注入
- 代码
public class UserServiceImpl implements UserService {
private UserDao userDao;// = new UserDaoImpl(); 体现了两层意思: a. new 某一个类的实例, b. 做出来的实例,赋值给dao.
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
@Override
public void save() {
System.out.println("调用UserServiceImpl的save方法~~" );
//UserDao userDao = new UserDaoImpl();
userDao.save();
}
}
- xml
<!-- 让spring托管这个UserDaoImpl ,以便它能够创建该类的实例, 是为了完成下面的注入 -->
<bean id="ud" class="com.itheima.dao.impl.UserDaoImpl"></bean>
<bean id="us" class="com.itheima.service.impl.UserServiceImpl" >
<!-- 指定了UserServiceImpl里面有一个属性叫做userDao ,它需要被注入, 注入的数据来自于 ref 里面声明的ud
spring会拿着ud 找到对应的bean, 接着做出来实例,然后注入给userDao -->
<property name="userDao" ref="ud"></property>
</bean>
2. 注解方式
依赖注入使用注解来实现 , DI的注解一般使用两个 @Resource & @Autowired
- 常用的注解就两个 @Resource & @Autowired
@Resource(name="ud") 根据给定的标记找到对应的类,创建对象,注入进来。
@Autowired 自动装配,会找到对应的实现类创建对象,注入进来。但是如果存在多个实现,那么会抛出异常
@Repository("ud")
public class UserDaoImpl implements UserDao {
}
public class UserServiceImpl implements UserService {
@Resource(name="ud") //spring拿着ud找到具体的类,然后创建实例,注入进来。
private UserDao userDao;
...
}
----------------------------------------------------------
public class UserServiceImpl implements UserService {
@Autowired //自动装配 根据注入的接口类型找到对应的实现类,注入进来。
private UserDao userDao;
...
}
三、 AOP
1. 什么是AOP ? 它有什么用?
AOP(Aspect Oriented Programming,面向切面编程), 可以说是OOP(Object Oriented Programing,面向对象编程)的补充和完善。在不改动源码的前提下,对原有的功能进行扩展 | 升级
2. AOP的底层原理
在java的世界里,能够不改动源码,但是又能做出扩展|增强的,不多。有装饰者模式, 有代理模式(静态代理 + 动态代理。) aop的底层采用的是: 动态代理。之所以不用装饰者模式或者静态代理,那是因为这里两种方式,都需要我们写实实在在的扩展类(装饰类 + 代理类) 。
- 代理回顾
3. 动态代理的实现方式
- 基于JDK的方式
针对的是被代理类有实现某一个接口,底层创建接口的另一个实现类作为代理类
//jdk动态代理
@Test
public void testJDKPorxy(){
//UserService userService = new UserServiceImpl();
//userService.save();
//1. 先创建真实对象
final UserService userService = new UserServiceImpl();
//2. 创建代理对象
UserService proxyObj = (UserService) Proxy.newProxyInstance(
userService.getClass().getClassLoader(), //类加载器,真实类用什么,代理类就用什么
userService.getClass().getInterfaces(), //真实类实现什么接口,代理类也实现什么接口
new InvocationHandler() {//回调函数
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("invoke~");
//userService.save();
if(method.getName().equals("save")){
Logger.log();
}
//以不变应万变。 反射调用
return method.invoke(userService, args);
}
});
//3. 让代理对象干活
proxyObj.save(); //代理对象。save() ----> 真实对象.save();
}
- 基于Cglib动态代理
如果真实类是一个普通类,没有实现接口,那么就采用这种方式, 创建出来真实类的子类作为代理类。
//cglib动态代理
@Test
public void testCglibPorxy(){
//1. 一定要有真实对象
final ProductService productService = new ProductService();
//2. 创建代理
Enhancer enhancer = new Enhancer();
//设置父类是谁
enhancer.setSuperclass(ProductService.class);
//设置回调
enhancer.setCallback(new MethodInterceptor() {
/*
* arg0 :代理对象
* arg3 : 方法的代理
*
* 一般这两不用。
*
* arg1 : 方法引用
* arg2 :参数
*/
@Override
public Object intercept(Object arg0, Method arg1, Object[] arg2, MethodProxy arg3) throws Throwable {
Logger.log();
return arg1.invoke(productService, arg2);
}
});
//创建代理对象
ProductService proxyObj = (ProductService) enhancer.create();
proxyObj.save();
}
3. AOP术语
4.AOP的入门
Spring的AOP其实已经准备好了创建代理的代码。只是不知道的是要创建谁的代码。哪些方法需要被增强。我们需要通过配置的形式告诉spring。
- 定义业务逻辑类
public class UserServiceImpl implements UserService {
@Override
public void save() {
System.out.println("调用了UserServiceImpl 的 save方法");
}
}
- 定义增强类
public class Logger {
public static void log(){
System.out.println("输出日志了~~");
}
}
- 添加依赖
compile 'org.springframework:spring-context:4.3.17.RELEASE'
//aspectJ 依赖包。
compile group: 'org.aspectj', name: 'aspectjweaver', version: '1.8.10'
- xml中配置
要导入aop的约束
让spring托管 业务逻辑类 和 增强类
<bean id="us" class="com.itheima.service.impl.UserServiceImpl" ></bean>
<bean id="logger" class="com.itheima.util.Logger" ></bean>
配置AOP
<!-- 2. 开始配置aop -->
<aop:config>
<!-- 配置切入点 expression 表达式 '
execution(* com.xyz.myapp.service.*.*(..))
execution 固定写法
第一个* 代表任意返回值
com.xyz.myapp.service : 包名
第二个* 包下的任意类
第三个* 类中的任意方法
(..) : 任意参数
saveUser
saveOrder
-->
<aop:pointcut expression="execution(* com.itheima.service.impl.*.*(..))" id="aa"/>
<!-- 配置增强
根据aa的表达式找到的方法,都给他们做前置增强,增强的功能是log方法
-->
<aop:aspect ref="logger">
<aop:before method="log" pointcut-ref="aa"/>
</aop:aspect>
</aop:config>
5. AOP 增强
<aop:config>
<aop:pointcut expression="execution(* com.itheima.service.impl.*.*(..))" id="pointcut01"/>
<!-- 以后去企业写代码,真正用aop来扩展一个功能,比较少。 除非是我们想扩展第三方jar包。
aop的思想无处不在: struts 拦截器 (就是AOP) -->
<!-- 配置切面aspect -->
<aop:aspect ref="logger">
<!-- 前置增强 -->
<!-- <aop:before method="log" pointcut-ref="pointcut01"/> -->
<!-- 最终增强 -->
<!-- <aop:after method="log" pointcut-ref="pointcut01"/> -->
<!-- 后置增强 -->
<!-- <aop:after-returning method="log" pointcut-ref="pointcut01"/> -->
<!-- 异常增强 -->
<!-- <aop:after-throwing method="log" pointcut-ref="pointcut01"/> -->
<!-- 环绕增强 -->
<!-- <aop:around method="around" pointcut-ref="pointcut01"/> -->
<aop:before method="log" pointcut-ref="pointcut01"/>
<aop:after-returning method="log" pointcut-ref="pointcut01"/>
</aop:aspect>
</aop:config>