Spring入门

一、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对象
img01.png
  • 演变成工厂
img02.png
  • spring ioc托管
img03.png

三、Spring入门

1. 入门案例

  1. 添加依赖
 compile 'org.springframework:spring-context:4.3.17.RELEASE'
  1. 编写业务逻辑类
public interface UserService {
    void save();
}

public class UserServiceImpl implements UserService {
    @Override
    public void save() {
        System.out.println("调用UserServiceImpl的save方法~~");
    }
}
  1. 创建配置文件 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>
  1. 在代码里面通过工厂获取对象
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>
  • 细节
  1. 为了迎合三层结构的趋势,Spring 为每一个层级制定了各自的注解,

​ controller层使用 @Controller

​ service层使用@Service

​ dao层使用@Repository

  1. 如果需要给指定的bean添加标识,可以使用注解属性追加 如: @service("userService")

  2. 默认情况下生成的实例还是单例,如果需要做成多例,需要额外加上 @Scope("prototype")

五、依赖注入

DI : 全称是 dependency Injection 翻译过来是依赖注入 本意是在实例化对象的时候,对它里面的成员属性进行值的注入。

public class UserServiceImpl{

private String address;

}

对这个address进行赋值: 有几种方式: 两种

  1. 有参构造

  2. 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,面向对象编程)的补充和完善。在不改动源码的前提下,对原有的功能进行扩展 | 升级

001.png

2. AOP的底层原理

在java的世界里,能够不改动源码,但是又能做出扩展|增强的,不多。有装饰者模式, 有代理模式(静态代理 + 动态代理。) aop的底层采用的是: 动态代理。之所以不用装饰者模式或者静态代理,那是因为这里两种方式,都需要我们写实实在在的扩展类(装饰类 + 代理类) 。

  • 代理回顾
002.png

3. 动态代理的实现方式

003.png
  • 基于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术语

004.png

4.AOP的入门

Spring的AOP其实已经准备好了创建代理的代码。只是不知道的是要创建谁的代码。哪些方法需要被增强。我们需要通过配置的形式告诉spring。

  1. 定义业务逻辑类
public class UserServiceImpl implements UserService {

    @Override
    public void save() {
        System.out.println("调用了UserServiceImpl 的 save方法");
    }
}
  1. 定义增强类
public class Logger {
    public static void log(){
        System.out.println("输出日志了~~");
    }
}
  1. 添加依赖
    compile 'org.springframework:spring-context:4.3.17.RELEASE'

    //aspectJ 依赖包。
    compile group: 'org.aspectj', name: 'aspectjweaver', version: '1.8.10'
  1. 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>

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 86,947评论 14 122
  • 1.1 spring IoC容器和beans的简介 Spring 框架的最核心基础的功能是IoC(控制反转)容器,...
    simoscode阅读 4,106评论 2 21
  • 本文是我自己在秋招复习时的读书笔记,整理的知识点,也是为了防止忘记,尊重劳动成果,转载注明出处哦!如果你也喜欢,那...
    波波波先森阅读 9,481评论 5 79
  • 1.1 Spring IoC容器和bean简介 本章介绍了Spring Framework实现的控制反转(IoC)...
    起名真是难阅读 1,415评论 0 8
  • 青春是场扑朔迷离的戏 快乐却是重蹈覆辙的苦 生活就像是一朵云包住了所有的阴雨 也就是给了一块入口即化的糖 又留下了...
    illme阅读 105评论 0 2