spring核心ioc、aop、动态代理

发布时间 2023-10-17 14:50:38作者: louvice

spring

1、快速入门

// 1、创建一个javabean
package com.spring.bean;

public class Monster {

    private Integer monsterId;
    private String name;
    private String skill;

    public Monster(Integer monsterId, String name, String skill) {
        this.monsterId = monsterId;
        this.name = name;
        this.skill = skill;
    }

    // spring需要一个空构造器
    public Monster() {
    }

    public Integer getMonsterId() {
        return monsterId;
    }

    public void setMonsterId(Integer monsterId) {
        this.monsterId = monsterId;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getSkill() {
        return skill;
    }

    public void setSkill(String skill) {
        this.skill = skill;
    }
}
@Override
    public String toString() {
        return "Monster{" +
                "monsterId=" + monsterId +
                ", name='" + name + '\'' +
                ", skill='" + skill + '\'' +
                '}';
    }

// 2、创建一个spring 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 class="com.spring.bean.Monster" id="monster01"></bean> // 加载JavaBean,在spring中id是唯一的
</beans>

    // 2.1 给JavaBean赋值
    <bean class="com.spring.bean.Monster" id="monster01">
        <property name="monsterId" value="1001"></property>
        <property name="name" value="牛魔王!"></property>
        <property name="skill" value="芭蕉扇!"></property>
    </bean>

    // 2.2 添加unit测试类,用于测试
    package com.spring.test;
    import com.spring.bean.Monster;
    import org.junit.Test;
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.support.ClassPathXmlApplicationContext;

    public class SpringBeanTest {

        @Test
        public void getMonster() {
            // 创建容器 ApplicationContext(接口)
            ApplicationContext ioc = new ClassPathXmlApplicationContext("beans.xml");// 类加载路径

            // 获取对象
            // 默认返回Object,运行内存是Monster
            //将Object转成Monster,方便用get方法
            Monster monster01 = (Monster) ioc.getBean("monster01"); 
            System.out.println(monster01 + "\n" + monster01.getClass());

            Monster monster011 = ioc.getBean("monster01", Monster.class);// 直接反射返回Monster对象

        }
    }

2、类的加载路径

// beanDefinitionNames记载id
String[] beanDefinitionNames = ioc.getBeanDefinitionNames();
for (String beanDefinitionName : beanDefinitionNames) {
    System.out.println(beanDefinitionName);
}


3、实现xml配置功能

package com.spring.LouApplicationContext;
import com.spring.bean.Monster;
import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import java.io.File;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;

public class Application {

    private ConcurrentHashMap<String, Object> singletonObject = new ConcurrentHashMap<>();

    public Application(String iocBeanXmlFile) throws Exception {

        // 类加载路劲
        String path = this.getClass().getResource("/").getPath();

        SAXReader saxReader = new SAXReader();

        Document document = saxReader.read(new File(path + iocBeanXmlFile));

        Element rootElement = document.getRootElement();

        Element bean = (Element)rootElement.elements("bean").get(0);

        String id = bean.attributeValue("id");
        String aClass = bean.attributeValue("class");

        List<Element> property = bean.elements("property");

        Integer monsterId = Integer.parseInt(property.get(0).attributeValue("value"));

        String name = property.get(1).attributeValue("value");
        String skill = property.get(2).attributeValue("value");

        Class<?> aClass1 = Class.forName(aClass);
        Monster o = (Monster)aClass1.newInstance();

        o.setMonsterId(monsterId);
        o.setName(name);
        o.setSkill(skill);

        singletonObject.put(id, o);

    }

    public Object getBean(String id){
        return singletonObject.get(id);
    }

}

4、基于xml配置bean

4.1 通过bean的类型来获取

// 1、通过bean的类型来获取
public void getBeanByType(){
    ApplicationContext ioc = new ClassPathXmlApplicationContext("beans.xml");

    //bean的类型只能是唯一
    Monster monster = ioc.getBean(Monster.class);
    System.out.println(monster);
}

4.2 通过构造器来配置bean

// 2、通过构造器来配置bean
    // 2.1 index来绑定
    <bean id="monster03" class="com.spring.bean.Monster">
        <constructor-arg value="200" index="0"/>
        <constructor-arg value="白骨精" index="1"/>
        <constructor-arg value="吸人血" index="2"/>
    </bean>
    // 2.2 name来绑定
    <bean id="monster03" class="com.spring.bean.Monster">
        <constructor-arg value="200" name="monsterId"/>
        <constructor-arg value="白骨精" name="name"/>
        <constructor-arg value="吸人血" name="skill"/>
    </bean>
    // 2.3 通过构造器变量的数据类型,来绑定构造器(构造器有多个,但是变量类型不能相同)
    <bean id="monster05" class="com.spring.bean.Monster">
        <constructor-arg value="200" type="java.lang.Integer"/>
        <constructor-arg value="白骨精" type="java.lang.String"/>
        <constructor-arg value="吸人血" type="java.lang.String"/>
    </bean>

4.3 通过p名称来配置bean

// 3、通过p名称来配置bean
<bean id="monster06" class="com.spring.bean.Monster"
    p:monsterId="500"
    p:name="红孩儿"
    p:skill="吐火"
/>         

4.4 通过ref来配置

// 4、通过ref来配置
<!--// 配置MemberDAOImpl对象-->
<bean class="com.spring.dao.MemberDAOImpl" id="memberDAO"/>

<!-- 配置MemberServiceImpl对象-->
<bean class="com.spring.service.MemberServiceImpl" id="memberService">
    <!--
    ref="memberDAO"表示,引用的对象是id=memberDAO的对象
    -->
    <property name="memberDAO" ref="memberDAO"/>
</bean>
    
package com.spring.service;

import com.spring.dao.MemberDAOImpl;

public class MemberServiceImpl {

    private MemberDAOImpl memberDAO;

    public MemberDAOImpl getMemberDAO() {
        return memberDAO;
    }

    public void setMemberDAO(MemberDAOImpl memberDAO) {
        this.memberDAO = memberDAO;
    }

    public void add(){
        memberDAO.add();
    }
}

4.5 通过内部bean来配置属性

// 5、通过内部bean来配置属性
<bean class="com.spring.service.MemberServiceImpl" id="memberService2">
<property name="memberDAO">
    <bean class="com.spring.dao.MemberDAOImpl"/>
</property>
</bean>

4.6 对集合/数组类型属性进行赋值

// 6、对集合/数组类型属性进行赋值
	// 6.1 定义master类 
    package com.spring.bean;

    import java.util.*;

    public class Master {
        private String name;
        private List<Monster> monsterList;
        private Map<String, Monster> monsterMap;
        private Set<Monster> monsterSet;

        // 数组
        private String[] monsterName;

        private Properties pros;

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }

        public List<Monster> getMonsterList() {
            return monsterList;
        }

        public void setMonsterList(List<Monster> monsterList) {
            this.monsterList = monsterList;
        }

        public Map<String, Monster> getMonsterMap() {
            return monsterMap;
        }

        public void setMonsterMap(Map<String, Monster> monsterMap) {
            this.monsterMap = monsterMap;
        }

        public Set<Monster> getMonsterSet() {
            return monsterSet;
        }

        public void setMonsterSet(Set<Monster> monsterSet) {
            this.monsterSet = monsterSet;
        }

        public String[] getMonsterName() {
            return monsterName;
        }

        public void setMonsterName(String[] monsterName) {
            this.monsterName = monsterName;
        }

        public Properties getPros() {
            return pros;
        }

        public void setPros(Properties pros) {
            this.pros = pros;
        }

        @Override
        public String toString() {
            return "Master{" +
                    "name='" + name + '\'' +
                    ", monsterList=" + monsterList +
                    ", monsterMap=" + monsterMap +
                    ", monsterSet=" + monsterSet +
                    ", monsterName=" + Arrays.toString(monsterName) +
                    ", pros=" + pros +
                    '}';
        }
    }

    // 6.2 定义xml文件
    <bean class="com.spring.bean.Master" id="master">
        <property name="name" value="太上老君"/>
        <property name="monsterList">
            <list>
                <ref bean="monster01"/>
                <ref bean="monster03"/>
                // 定义内部bean
                <bean class="com.spring.bean.Monster">
                    <property name="name" value="黄袍怪"/>
                    <property name="skill" value="杀人"/>
                    <property name="monsterId" value="100"/>
                </bean>
            </list>
        </property>
    </bean>    

4.7 对集合/数组类型属性进行赋值

// 1、数组类型属性进行赋值
<list>
    <ref bean="monster01"/>
    <ref bean="monster03"/>
    <bean class="com.spring.bean.Monster">
        <property name="name" value="黄袍怪"/>
        <property name="skill" value="杀人"/>
        <property name="monsterId" value="100"/>
    </bean>
</list>



// 2、集合方式进行赋值
<property name="monsterMap">
    <map>
        <entry>
            <key>
                <value>monster001</value>
            </key>
            <ref bean="monster03"/>
        </entry>
        <entry>
            <key>
                <value>monster002</value>
            </key>
            <ref bean="monster01"/>
        </entry>
    </map>
</property>

4.8 set属性赋值

// 对象直接依赖注入
<property name="monsterSet">
    <set>
        <ref bean="monster04"/>
        <ref bean="monster01"/>
    </set>
</property>

4.9 Array属性赋值

// 字符串可以直接赋值
<property name="monsterName">
    <array>
        <value>小妖怪</value>
        <value>老妖怪</value>
    </array>
</property>

4.10 Properties赋值

// key-value进行存放,以字符串的形式(财产的意思)
<property name="pros">
    <props>
        <prop key="username">root</prop>
        <prop key="password">123456</prop>
        <prop key="ip">127.0.0.1</prop>
    </props>
</property>

4.11 util创建list

// 提高数据的复用性
<util:list id="myBookList">
    <value>三国演义</value>
    <value>红楼梦</value>
    <value>西游记</value>
    <value>水浒传</value>
</util:list>

<bean class="com.spring.bean.BookStore" id="bookStore">
    <property name="bookList" ref="myBookList"/>
</bean>

4.12 级联属性赋值

<bean class="com.spring.bean.Dept" id="dept"/>

<bean class="com.spring.bean.Emp" id="emp">
    <property name="name" value="jack"/>
    <property name="dept" ref="dept"/>
    <!--级联属性赋值,调用dept.setName方法-->
    <property name="dept.name" value="Java开发部门"/>
</bean>

4.13 beanfactory获取bean

<!--
1、通过静态工厂获取
2、class是静态工厂的全路径
3、factory-method表示指定静态工厂类的哪个方法返回对象
4、value是指定返回静态工厂的哪个对象
-->
<bean id="my_monster01" class="com.spring.factory.MyStaticFactory" factory-method="getMonster">
    <constructor-arg value="monster02"/>
</bean>
    
    
package com.spring.factory;

import com.spring.bean.Monster;

import java.util.HashMap;
import java.util.Map;

public class MyStaticFactory {

    private static Map<String, Monster> monsterMap;

    //使用static代码块进行初始化
    static {
        monsterMap = new HashMap<>();
        monsterMap.put("monster01",new Monster(100,"牛魔王","芭蕉扇"));
        monsterMap.put("monster01",new Monster(102,"狐狸精","好看"));
    }

    //返回一个对象,单类模式
    public static Monster getMonster(String key){
        return monsterMap.get(key);
    }
}

4.14 InstanceFactory实例工厂

// 1、定义一个实例工厂类
package com.spring.factory;
import com.spring.bean.Monster;
import java.util.HashMap;
import java.util.Map;

public class MyInstanceFactory {

    private Map<String, Monster> monster_Map;

    // 通过普通代码块进行初始化,每次初始化类的时候,都会执行一次
    {
        monster_Map = new HashMap<>();
        monster_Map.put("monster03", new Monster(300, "牛魔王", "芭蕉扇"));
        monster_Map.put("monster04", new Monster(400, "狐狸精", "好看"));
    }

    // 写一个方法返回Monster对象
    public  Monster getMonster(String key) {
        return monster_Map.get(key);
    }
}

// 2、配置xml文件
<!--
1、配置monster对象,实例工厂
2、factory-bean指定哪个实例工厂对象的返回bean
3、factory-method表示指定静态工厂类的哪个方法返回对象
-->
<bean class="com.spring.factory.MyInstanceFactory" id="myInstanceFactory"/>

<bean id="my_monster02" factory-bean="myInstanceFactory" factory-method="getMonster">
    <constructor-arg value="monster03"/>
</bean>

4.15 FactoryBean获取bean

package com.spring.factory;

import com.spring.bean.Monster;
import org.springframework.beans.factory.FactoryBean;

import java.util.HashMap;
import java.util.Map;

public class MyFactoryBean implements FactoryBean {

    private String key;
    private Map<String, Monster> monster_Map;

    {
        monster_Map = new HashMap<>();
        monster_Map.put("monster03", new Monster(300, "牛魔王", "芭蕉扇"));
        monster_Map.put("monster04", new Monster(400, "狐狸精", "好看"));
    }

    @Override
    public Object getObject() throws Exception {
        return monster_Map.get(key);
    }

    public void setKey(String key) {
        this.key = key;
    }

    @Override
    public Class<?> getObjectType() {
        return Monster.class;
    }

    @Override
    public boolean isSingleton() {
        return true;
    }
}

// xml文件配置
<bean id="my_monster05" class="com.spring.factory.MyFactoryBean">
    <property name="key" value="monster04"/>
</bean>

4.16 Extend Bean

// Extend
<bean id="monster10" class="com.spring.bean.Monster">
    <property name="name" value="蜈蚣精"/>
    <property name="monsterId" value="1000"/>
    <property name="skill" value="蜇人"/>
</bean>

<bean id="monster11" class="com.spring.bean.Monster" parent="monster10"/>
    
// tips    
<!--
1、如果被指定abstract,表示该bean对象,适用于继承
2、这个bean不能被被实例化
-->
<bean id="monster12" class="com.spring.bean.Monster" abstract="true"/>

4.17 Bean的单类和多类

scope 范围
<!--
1、在默认情况下,属性是单类。scope
2、在ioc中,只有一个bean对象
3、getBean时,返回的是同一个对象
4、返回一个新的Bean对象,则可以scope="property"
-->
<bean id="cat" class="com.spring.bean.Cat" scope="singleton">
    <property name="id" value="100"/>
    <property name="name" value="小花猫"/>
</bean>

// scope="singleton" lazy-init="true" 此时,不会预先加载对象(ioc内不会存在),getBean才会
// 默认懒加载是关闭的
<bean id="cat" class="com.spring.bean.Cat" scope="singleton" lazy-init="true">
    <property name="id" value="100"/>
    <property name="name" value="小花猫"/>
</bean>

5、bean的生命周期

bean对象是由jvm完成
package com.spring.bean;

public class House {

    private String name;

    public House() {
    }

    public House(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
        System.out.println("set方法" + name);
    }

    public void init(){
        System.out.println("house init()");
    }

    public void destroy(){
        System.out.println("house destroy");
    }
}

<!--
1、init-method指定初始化方法,在setter方法后执行
2、方法执行的时机,由spring容器控制
3、destroy-method="" 容器关闭的时候触发
-->
<bean class="com.spring.bean.House" id="house" init-method="init" destroy-method="destroy">
    <property name="name" value="北京"/>
</bean>

6、bean的后置处理器

// 创建处理器,必须实现接口implements BeanPostProcessor
package com.spring.bean;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;

public class MyBeanPostProcessor implements BeanPostProcessor {


    // 在bean的init方法前被调用(xml文件配置的init方法)
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("postProcessBeforeInitialization().. bean=" + bean + beanName);
        return null;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("postProcessAfterInitialization().. bean=" + bean + beanName);
        return null;
    }

}

// xml文件配置
<bean class="com.spring.bean.House" id="house" init-method="init" destroy-method="destroy">
    <property name="name" value="豪宅"/>
</bean>

<!--配置后置处理器对象
1、当我们在bean02.xml容器配置文件,配置了MyBeanPostProcessor
2、后置处理器,就会作用在该容器的所有对象
-->
<bean class="com.spring.bean.MyBeanPostProcessor" id="MyBeanPostProcessor"/>
    

7、属性文件配置

// 配置文件赋值bean
<!--配置文件配置bean
    1、指定配置文件 location
-->
<context:property-placeholder location="classpath:my.properties"/>
<bean class="com.spring.bean.Monster" id="monster1000">
    <property name="monsterId" value="${monsterId}"/>
    <property name="name" value="${name}"/>
    <property name="skill" value="${skill}"/>
</bean>

my.properties    
monsterId=1000
name=jack  (中文的话,转成unicode编码)
skill=hello
    

8、自动装配

<bean class="com.spring.dao.OrderDao" id="orderDao"/>

<!--配置OrderService对象
    1、autowire="byType"表示在创建orderService时,通过类型的方式,给对象属性自动完成赋值
    2、orderService中有private OrderDao orderDao
    3、就会在容器中找有没有OrderDao类型对象
    4、不能同时有两个,相同类型的对象
-->

<bean autowire="byType" class="com.spring.service.OrderService" id="orderService"/>

<bean autowire="byType" class="com.spring.web.OrderAction" id="orderAction"/>

9、注解配置bean

1、Component 表示注解标识的是一个组件

2、Controller 表示当前注解标识是一个控制器,通常用于Servlet

3、Service 表示当前注解标识的是一个处理业务逻辑的类,通常用于Servlet类

4、Repository 表示当前注解标识的是一个持久化层的类,通常用于Dao类

9.1 快速入门

// 持久化层的类
@Repository
public class UserDao {

}

// 业务逻辑
@Service
public class UserService {
}

// 控制器
@Controller
public class UserAction {
}

// 标识为组件
@Component
public class MyComponent {
}

// xml配置
<context:component-scan base-package="com.spring.component"/>
   

9.2 自己实现注解bean

// 1、创建注解
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface ComponentScan {
    String value() default "";
}

// 2、创建注解加载配置文件
package com.spring.annotation;

@ComponentScan(value = "com.spring.component")
public class LouSpringConfig {

}

// 3、创建SpringApplicationContext
public class LouSpringApplicationContext {

    private Class configClass;
    private final ConcurrentHashMap<String, Object> ioc = new ConcurrentHashMap<>();

    public LouSpringApplicationContext(Class configClass) throws ClassNotFoundException, IllegalAccessException, InstantiationException {
        this.configClass = configClass;

        //    1、得到LouSpringConfig 配置的@ComponentScan(value = "com.spring.annotation")
        ComponentScan componentScan = (ComponentScan) this.configClass.getDeclaredAnnotation(ComponentScan.class);
        String path = componentScan.value();
        System.out.println("要扫描的包=" + path);

        //    得到类的加载器
        ClassLoader classLoader = LouSpringApplicationContext.class.getClassLoader();

        // 替换一下路径里面的.
        //path = path.replace(".", "/");
        //System.out.println(path);
        URL resource = classLoader.getResource("com/spring/component");

        System.out.println("URL=" + resource);

        // 将要加载的资源路径下的文件,进行遍历
        File file = new File(resource.getFile());
        if (file.isDirectory()) {
            File[] files = file.listFiles();
            for (File f : files) {
                //System.out.println("========");
                //System.out.println(f.getAbsolutePath());

                String fileAbsolutePath = f.getAbsolutePath();
                if (fileAbsolutePath.endsWith(".class")) {
                    String className =
                            fileAbsolutePath.substring(fileAbsolutePath.lastIndexOf("\\") + 1, fileAbsolutePath.indexOf(".class"));

                    String classFullName = path + "." + className;
                    System.out.println(classFullName);

                    // 判断该类是不是需要注入容器
                    Class<?> aClass = classLoader.loadClass(classFullName);
                    if (aClass.isAnnotationPresent(Component.class) ||
                            aClass.isAnnotationPresent(Controller.class) ||
                            aClass.isAnnotationPresent(Service.class) ||
                            aClass.isAnnotationPresent(Repository.class)
                    ) {
                        // 完整的反射一个类
                        Class<?> aClass1 = Class.forName(classFullName);
                        Object instance = aClass1.newInstance();

                        ioc.put(StringUtils.uncapitalize(className), instance);
                    }
                }

            }
        }


    }
    public Object getBean(String name){
        return ioc.get(name);
    }
}

10、自动装配

// 自动加载userService类
public class UserAction {
    @Autowired
    private UserService userService;

    public void sayOk(){
        System.out.println("UserAction sayOK");
        userService.hi();
    }
}

// @Resource
@Controller
public class UserAction {

    // @Resource(name = "userService")表示装配id=userService 对象
    @Resource(name = "userService")
    private UserService userService400;

    public void sayOk(){
        System.out.println("UserAction sayOK");
        userService400.hi();
    }
}

11、动态代理

# 动态代理,核心代码
package com.spring.proxy2;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class VehicleProxyProvider {

    private Vehicle target_vehicle;


    public VehicleProxyProvider(Vehicle target_vehicle) {
        this.target_vehicle = target_vehicle;
    }

    public Vehicle getProxy(){

        ClassLoader classLoader = target_vehicle.getClass().getClassLoader();
        Class<?>[] interfaces = target_vehicle.getClass().getInterfaces();

        InvocationHandler invocationHandler = new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                System.out.println("交通工具开始运行了");
                Object result = method.invoke(target_vehicle, args);
                System.out.println("交通工具停止运行了");
                return result;
            }
        };
        //public static Object newProxyInstance(ClassLoader loader,
        //        Class<?>[] interfaces,
        //        InvocationHandler h)

        Vehicle proxy = (Vehicle)Proxy.newProxyInstance(classLoader, interfaces, invocationHandler);
        return proxy;
    }
}