Bootstrap

Spring框架

一、概念

spring是轻量级的开源javaee框架,Spring可以解决企业应用开发的复杂性。

spring有两个核心的部分:IOC和Aop

IOC,控制反转,把创建的对象过程交给Spring进行管理

Aop,在不修改代码的前提下进行功能的增强。

Spring特点

  • 方便解耦,简化开发
  • 方便测试
  • 方便和其他框架整合
  • 方便进行事务操作
  • 降低API开发难度

二、IOC容器

2.1、IOC的作用

把对象的创建和对象的调用过程交给Spring处理,这样可以降低耦合度。

在此之前我们需要导入相关的依赖,如果使用maven,直接导入依赖,或者导入相对应的jar包。

使用Spring创建对象

两种方式:基于xml配置文件,基于注解

(1)基于注解创建对象

1)创建对象

创建User类

public class User {
    public void add(){
        System.out.println("add....");
    }
}

 在src下创建一个bean.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">
<!--    配置User对象属性  id方便找到对象. class对应要创建对象类的全路径(包 + 类名称)-->
    <bean id="user" class="com.wyy.spring5.User">
 
    </bean>

创建一个Test包,在包里创建一个TestSpring类,用的单元测试

    @Test
    public void testAdd(){
        //1.加载spring配置文件,填写干刚刚创建的bean文件
        BeanFactory context = new ClassPathXmlApplicationContext("bean1.xml");
        //2.获取配置创建的对象,user是我们在配置bean时起的id
        User user = context.getBean("user", User.class);
        //3、调用相应的方法
        user.add();
    }

 Spring提供IOC容器实现两种方式:

  • (1)BeaFactory:IOC容器基本实现,是Spring内部使用,加载配置文件时候不会闯将对象,在获取的时候才会创建。
  • (2)ApplicationContext:BeanFactory的子接口,多功能,面向开发人员使用。加载时候就创建。
  • ApplicationContext主要实现类,
    •  FileSystemXmlApplicationContext
    • ClassPathXmlApplicationContext

运行结果

add....

2)成员变量属性配置注入

①利用set注入

创建Book类

public class Book {
    private String bname;
    private String bauthor;
    public void setBname(String bname) {
        this.bname = bname;
    }

    public void setBauthor(String bauthor) {
        this.bauthor = bauthor;
    }

   public void testDemo(){
       System.out.println(bname + ":" + bauthor);
   }
}

在bean文件中进行配置

    <bean id="book" class="com.wyy.spring5.Book">
        <!--<property name=“属性名” value=“属性值”> -->
        <property name="bname" value = "《三国演义》"></property>
        <property name="bauthor" value = "施耐庵"></property>
    </bean>

Test类

    @Test
    public void testBook1(){
        ApplicationContext context = new ClassPathXmlApplicationContext("bean1.xml");
        Book book = context.getBean("book",Book.class);
        book.testDemo();
    }

运行结果 

《三国演义》:施耐庵

 ②利用构造方法注入

创建Order类

public class Orders {
    private String oname;
    private String oaddress;
    private String oprice;
    
    //有参构造注入
    public Orders(String oname, String oaddress,String oprice){
        this.oname = oname;
        this.oaddress = oaddress;
        this.oprice = oprice;
    }

    public void order(){
        System.out.println(oname + ":" + oaddress + ":" + oprice);
    }
}

在bean中配置

    <bean id="orders" class="com.wyy.spring5.Orders">
        <!--<constructor-arg name=“属性名” value=“属性值”> -->
        <constructor-arg name="oname" value="电脑"></constructor-arg>
        <constructor-arg name="oaddress" value="china"></constructor-arg>
        <!--null值 用一个<null/>代替属性值value ,值位空 -->
        <constructor-arg name="oprice">
            <null/>
        </constructor-arg>
    </bean>

Test类 

    @Test
    public void testOeder1() {
        ApplicationContext context = new ClassPathXmlApplicationContext("bean1.xml");
        Orders orders = context.getBean("orders", Orders.class);
        orders.order();
    }

运行结果 

电脑:china:null

3)外部注入(对象注入)

创建Dao包,在包里创建UseDao接口类和UserDaoImpl实现类

public interface UserDao {
    public void update();
}

public class UserDaoImpl implements UserDao{
    @Override
    public void update() {
        System.out.println("dao  update...");
    }
}

创建Service包,在包内创建UserService类

public class UserService {
    //可以是属性,也可以是类对象
    //创建UseDao类型属性,生成set方法
    private UserDao userDao;

    public void setUserDao(UserDao userDao) {
        this.userDao = userDao;
    }

    public void add(){
        System.out.println("service add...");
        userDao.update();
    }
}

配置

因为是两个对象,我们需要建立两个bean来通过spring创建对象。

    <bean id="userService" class="com.wyy.spring5.Service.UserService"></bean>

   <bean id="userDaoImpl" class="com.wyy.spring5.Dao.UserDaoImpl"></bean>

UserService类中创建了UserDao属性变量,我们需要在实现UserService变量中注入UserDao对象

完整配置

    <bean id="userService" class="com.wyy.spring5.Service.UserService">
    <!--注入UserDao对象-->
        <property name="userDao" ref="userDaoImpl"></property>
    </bean>


    <bean id="userDaoImpl" class="com.wyy.spring5.Dao.UserDaoImpl">

    </bean>

Test类

    @Test
    public void testBean(){
        ApplicationContext context = new ClassPathXmlApplicationContext("bean2.xml");
        UserService userService = context.getBean("userService",UserService.class);
        userService.add();
    }

运行结果

service add...
dao  update...

通过注入对象,当对userService方法进行定义时,可以调用userDao类的方法。

4)集合类型注入

数据类型注入:value值放在array标签中

List集合类型注入:value值放在list标签中

Map类型注入:value值放在entry标签中,标签中有 key和value两个值

Stu类

public class Stu {
    //数组类型
    private String[] courses;

    //list集合类型
    private List<String> list;

    //Map集合类型
    private Map<String, String> map;

    //set集合
    private Set<String> sets;

    //学生所学的多门课程
    private List<Course> courseList;

    public void setCourses(String[] courses) {
        this.courses = courses;
    }

    public void setList(List<String> list) {
        this.list = list;
    }

    public void setMap(Map<String, String> map) {
        this.map = map;
    }

    public void setSets(Set<String> sets) {
        this.sets = sets;
    }

    public void setCourseList(List<Course> courseList) {
        this.courseList = courseList;
    }

    public void testGet() {
        System.out.println(Arrays.toString(courses));
        System.out.println(list);
        System.out.println(map);
        System.out.println(sets);
        System.out.println(courseList);
    }
}

Course类

public class Course {
    private String cname;

    public Course() {
    }

    public void setCname(String cname) {
        this.cname = cname;
    }

    @Override
    public String toString() {
        return "Course{" +
                "cname='" + cname + '\'' +
                '}';
    }
}

配置类 

<bean id="stu" class="com.wyy.spring5.collectiontype.Stu">
        <!-- 数组类型-->
        <property name="courses">
            <array>
                <value>数据库</value>
                <value>数据结构</value>
            </array>
        </property>

        <!--  List类型-->
        <property name="list">
            <list>
                <value>张三</value>
                <value>小三</value>
            </list>
        </property>

        <!--map类型-->
        <property name="map">
            <map>
                <entry key="JAVA" value="java"></entry>
                <entry key="PHP" value="php"></entry>
            </map>
        </property>

        <!--set注入-->
        <property name="sets">
            <set>
                <value>mysql</value>
                <value>Redis</value>
            </set>
        </property>

        <!--注入list集合, 放 值是对象-->
        <property name="courseList">
            <list>
                <ref bean="course1"></ref>
                <ref bean="course2"></ref>
            </list>
        </property>

    </bean>
    <!--创建多个course对象-->
    <bean id="course1" class="com.wyy.spring5.collectiontype.Course">
        <property name="cname" value="Spring框架"></property>
    </bean>
    <bean id="course2" class="com.wyy.spring5.collectiontype.Course">
        <property name="cname" value="MyBatis框架"></property>
    </bean>

test类

    @Test
    public void testArray(){
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("bean1.xml");
        Stu stu = applicationContext.getBean("stu", Stu.class);
        stu.testGet();
    }

运行结果

[数据库, 数据结构]
[张三, 小三]
{JAVA=java, PHP=php}
[mysql, Redis]
[Course{cname='Spring框架'}, Course{cname='MyBatis框架'}]

5)factoryBean接口:工厂Bean 

普通的bean中:spring中配置定义的是什么类型返回的就是什么类型

工厂bean:可以返回不同类型

定义MyBean类,实现FactorBean接口

//这里填写泛型为Course
public class MyBean implements FactoryBean<Course> {
    //定义返回bean
    @Override
    public Course getObject() throws Exception {
        Course course = new Course();
        course.setCname("abc");
        return course;
    }

    @Override
    public Class<?> getObjectType() {
        return null;
    }

    @Override
    public boolean isSingleton() {
        return FactoryBean.super.isSingleton();
    }
}

配置类

 <bean id="myBean" class="com.wyy.spring5.FactoryBean.MyBean"></bean>

Test类

    @Test
    public void test1(){
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("bean3.xml");
       //这里填写的是Course.class 是因为在Mybean中我们指定了泛型为Course,因为需要改写这里,否则报错,找不到相应类
       Course course = context.getBean("myBean", Course.class);
        System.out.println(course);
    }

运行结果

Course{cname='abc'}

(2)基于注解创建对象

Spring针对Bean管理中创建对象提供注解,注解功能一样 都可以用来创建bean实例。

  • (1)@Component 普通
  • (2)@Service 业务逻辑层
  • (3)@Cintoller web层
  • (4)@Repository dao层

主要操作:

(1)导入jar,或者使用maven,添加依赖到pom文件下

9ca120b02e3de517ef58814fe7a626f9.jpeg

(2)开启组件扫描

  • 先引入context命名空间
  • 扫描多个包,用逗号隔开 或 直接扫描上层目录
  <!--开启组件扫描-->
    <context:component-scan base-package="com.wyy"></context:component-scan>

(3)创建对象,在类上面添加注解

(4)开启组件扫描配置细节

添加use-default-filters=false表示不使用默认扫描,自己配置filter 只扫描本包下有Controller注解的类。

3772500115223a8188f5e8c284169ab3.jpeg

(5)基于注解方式实现属性注入

  • @AutoWired 根据属性类行进行自动装配
    •  第一步 创建service 和dao类,并添加注解​
    • 第二步 在service注入dao对象,在service类添加dao类型的属性,在属性上使用注解
  • (2)@Qualifer 根据属性名称进行注入 value
  • (3)@Resource 可以根据类型注入,可以根据名称注入 name
  • (4)@value 普通类型

演示:

配置文件 bean.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.wyy"></context:component-scan>

</beans>

Dao包 UserDao接口和UserDaoImpl实现类

public interface UseDao {
    public void add();
}

@Repository
public class UseDaoImpl implements UseDao{
    @Override
    public void add() {
        System.out.println("dao add...");
    }
}

Service包 UserService类

//注解中value属性值可以省略不写
//默认值是类名称,首字母小写
@Service(value = "userService") //value 的值等价于配置文件中bean中id的值
public class UseService {
    //不需要加set方法

    @Autowired
    private UseDao useDao;

    @Value(value = "abc")
    private String name;

    public void add(){
        System.out.println("service add..."  + name);
        useDao.add();
    }
}

Test包 test类

    @Test
    public void testAutoWire(){
        ApplicationContext context = new ClassPathXmlApplicationContext("bean1.xml");
        UseService useService = context.getBean("userService", UseService.class);
        System.out.println(useService);
        useService.add();
    }

运行结果

service add...abc
dao add...

 完全注解开发

(1)创建配置类 替代xml配置文件

@Configuration //把当前类作为xml配置文件
@ComponentScan(basePackages ={"com.wyy"})//类似扫描组件
public class SpringConfig {

}

(2)Test测试类

注意与前面不同

    @Test
    public void testConnfig(){
        ApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig.class);
        UseService useService = context.getBean("userService", UseService.class);
        System.out.println(useService);
        useService.add();
    }

运行结果

service add...abc
dao add...

 2.2 Aop 

Aop是面向切面编程,不通过修改源代码的方式,在主干功能里面添加新的功能

(1)AOP底层原理是使用动态代理,有两种动态代理方式

  • 第一种,有接口,使用JDK动态代理    
  • 第二种,无接口,使用CGIVB代理

(2)实现动态代理演示

JDk动态代理,使用Proxy类里面的方法创建动态代理类的对象,也调用newProxyInstance方法。

  • 第一个参数 类加载器 当前类接
  • 第二个参数 增强方法所在类的接口字节码,用数组形式来表示
  • 第三个参数 实现接口InvocationHandler ,创建代理对象,写增强方法 proxy实现类的实例对象。

userDao接口类

在接口种定义了两个方法,一个有参有返回值,一个无参无返回值,

public interface UserDao {
  public int add(int a, int b);
  public void update();
}

userDaoImpl实现类

对user中的方法进行实现。

public class UserDaoImpl implements UserDao{
    @Override
    public int add(int a, int b) {
        System.out.println("add执行了...");
        return a+b;
    }
    @Override
    public void update() {
        System.out.println("update执行了...");
    }
}

JDKProxy代理类(主要)

在类中定义一个给userDaoImpl实现类,创建一个代理。

public class JDKProxy {
    public static UserDao createProxy(UserDaoImpl userDao ){
        UserDao user = (UserDao)Proxy.newProxyInstance(JDKProxy.class.getClassLoader(),
                new Class[]{UserDao.class},//对应接口的数组
                new InvocationHandler() {
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        if("add".equals(method.getName())){
                            System.out.println("准备计算");
                        }else if ("update".equals(method.getName())){
                            System.out.println("准备更新");
                        }
                        //返回调用方法的返回值
                        return method.invoke(userDao ,args);
                    }
                });
        return user;
    }
}

Test类

    @Test
    public void test3(){
        //1.获取代理对象
        UserDaoImpl userDao = new UserDaoImpl();
        UserDao proxy = JDKProxy.createProxy(userDao);
        //2.调用方法 有参有返回值
        int add = proxy.add(2, 4);
        System.out.println(add);
        //3.调用方法 无惨无返回值
        proxy.update();
    }

运算结果

准备计算
add执行了...
6
准备更新
update执行了...

(3)AOP术语

  • 1)连接点: 类那些方法可以被增强,这些方法称为连接点
  • 2)切入点:实际被增强的方法,称为切入点
  • 3)通知(增强)
    • 实际增强的逻辑部分被叫做通知 代码
    • 类型
      • 前置通知:@before
      • 后置通知:@After
      • 环绕通知:前后都有 @Around
      • 异常通知:异常 @afterThrowing
      • 最终通知:无论怎样都会在最后进行通知 @afterReturning
      • 优先级:(从高到低)Around/ before /after /afterReturning/ afterThrowing
  • 4)切面:动作,把通知应用到切入点的过程

(4)Aop在Spring中的操作

1)spring框架一般都是基于AspectJ实现Aop操作,但AspectJ不属于Spring框架

2)基于AspectJ实现Aop操作

①基于xml配置

要被增强的Book类

public class Book {
    public void add(){
        System.out.println("add...");
    }
}

BookProxy代理类

public class BookProxy {
    public void before(){
        System.out.println("before.add..");
    }
}

配置类(关键)

<?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"
       xmlns:aop="http://www.springframework.org/schema/aop"
       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
                            http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
   
    <bean id="book" class="com.wyy.spring5.aopxml.Book"></bean>
    <bean id="bookProxyk" class="com.wyy.spring5.aopxml.BookProxy"></bean>
    <aop:config>
        <!--切入点-->
        <aop:pointcut id="p" expression="execution(* com.wyy.spring5.aopxml.Book.add(..))"/>
        <!--配置切面-->
        <aop:aspect ref="bookProxyk">
            <!--增强作用在具体的方法上-->
            <aop:before method="before" pointcut-ref="p"></aop:before>
        </aop:aspect>
    </aop:config>
</beans>

运行结果

before.add..
add...

 ②基于注解

配置类

<?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"
       xmlns:aop="http://www.springframework.org/schema/aop"
       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
                            http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
<!-- 开启扫描-->
    <context:component-scan base-package="com.wyy.spring5.aopannno"></context:component-scan>
<!--开启aspect生成代理对象-->
    <aop:aspectj-autoproxy></aop:aspectj-autoproxy>
</beans>

要被增强的User类

@Component
public class User {
    public void add(){
        System.out.println("add...");
    }
}

增强类UserProxy类

切入点表达式

切入点表达式作用:知道对哪个类里面的哪个方法进行增强

execution([权限修饰符][返回类型][类全路劲][方法名称]([参数列表]))

使用 .. 表示任意个数、任意类型的参数

@Component
@Aspect//生成代理对象
public class UserProxy {

    //前置通知 value是指要被增强类的方法
    @Before(value = "execution(* com.wyy.spring5.aopannno.User.add(..))")
    public void before(){
        System.out.println("before...");
    }
    
    @After(value = "execution(* com.wyy.spring5.aopannno.User.add(..))")
    public void after(){
        System.out.println("after...");
    }

    @AfterReturning(value = "execution(* com.wyy.spring5.aopannno.User.add(..))")
    public void afterReturning(){
        System.out.println("afterReturning...");
    }

    @AfterThrowing(value = "execution(* com.wyy.spring5.aopannno.User.add(..))")
    public void afterThrowing(){
        System.out.println("afterThrowing...");
    }
    
    //当被增强的方法是同一个时,可以对value的值进行抽取(相同切入点抽取)
    //@Pointcut(value = "execution(* com.wyy.spring5.aopannno.User.add(..))")
    //public void pointdemp(){}
    //之后可以这样写
    //@Before(value = pointdemp())
    //public void before(){
        //System.out.println("before...");
    //}
    
}

 Test类

    @Test
    public void test1(){
        ApplicationContext context = new ClassPathXmlApplicationContext("bean1.xml");
        User user = context.getBean("user", User.class);
        user.add();
    }

运行结果

before...
add...
after...
afterReturning...

 多个增强类对同一个方法进行增强,可以设置优先级,在增强类上面填注解 @order (数字类型值)数字越小 ,优先级越高。

完全注解

添加配置类

@Configuration
@ComponentScan(basePackages = {"com.wyy"})
@EnableAspectJAutoProxy(proxyTargetClass = true)//开启开启aspect生成代理对象
public class ConfigAop {
    
}

 

       

 

 

;