Spring的DI

bean.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
<?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">
 
    <!-- spring中的依赖注入
        依赖注入:
            Dependency Injection
        IOC的作用:
            降低程序间的耦合(依赖关系)
        依赖关系的管理:
            以后都交给spring来维护
        在当前类需要用到其他类的对象,由spring为我们提供,我们只需要在配置文件中说明
        依赖关系的维护:
            就称之为依赖注入。
         依赖注入:
            能注入的数据:有三类
                基本类型和String
                其他bean类型(在配置文件中或者注解配置过的bean)
                复杂类型/集合类型
             注入的方式:有三种
                第一种:使用构造函数提供
                第二种:使用set方法提供
                第三种:使用注解提供(明天的内容)
     -->
 
 
    <!--构造函数注入:
        使用的标签:constructor-arg
        标签出现的位置:bean标签的内部
        标签中的属性
            type:用于指定要注入的数据的数据类型,该数据类型也是构造函数中某个或某些参数的类型
            index:用于指定要注入的数据给构造函数中指定索引位置的参数赋值。索引的位置是从0开始
            name:用于指定给构造函数中指定名称的参数赋值                                        常用的
            =============以上三个用于指定给构造函数中哪个参数赋值===============================
            value:用于提供基本类型和String类型的数据
            ref:用于指定其他的bean类型数据。它指的就是在spring的Ioc核心容器中出现过的bean对象
 
        优势:
            在获取bean对象时,注入数据是必须的操作,否则对象无法创建成功。
        弊端:
            改变了bean对象的实例化方式,使我们在创建对象时,如果用不到这些数据,也必须提供。
    -->
    <bean id="accountService" class="com.itheima.service.impl.AccountServiceImpl">
        <constructor-arg name="name" value="泰斯特"></constructor-arg>
        <constructor-arg name="age" value="18"></constructor-arg>
        <constructor-arg name="birthday" ref="now"></constructor-arg>
    </bean>
 
    <!-- 配置一个日期对象 -->
    <bean id="now" class="java.util.Date"></bean>
 
 
 
    <!-- set方法注入                更常用的方式
        涉及的标签:property
        出现的位置:bean标签的内部
        标签的属性
            name:用于指定注入时所调用的set方法名称
            value:用于提供基本类型和String类型的数据
            ref:用于指定其他的bean类型数据。它指的就是在spring的Ioc核心容器中出现过的bean对象
        优势:
            创建对象时没有明确的限制,可以直接使用默认构造函数
        弊端:
            如果有某个成员必须有值,则获取对象是有可能set方法没有执行。
    -->
    <bean id="accountService2" class="com.itheima.service.impl.AccountServiceImpl2">
        <property name="name" value="TEST" ></property>
        <property name="age" value="21"></property>
        <property name="birthday" ref="now"></property>
    </bean>
 
 
    <!-- 复杂类型的注入/集合类型的注入
        用于给List结构集合注入的标签:
            list array set
        用于个Map结构集合注入的标签:
            map  props
        结构相同,标签可以互换
    -->
    <bean id="accountService3" class="com.itheima.service.impl.AccountServiceImpl3">
        <property name="myStrs">
            <set>
                <value>AAA</value>
                <value>BBB</value>
                <value>CCC</value>
            </set>
        </property>
 
        <property name="myList">
            <array>
                <value>AAA</value>
                <value>BBB</value>
                <value>CCC</value>
            </array>
        </property>
 
        <property name="mySet">
            <list>
                <value>AAA</value>
                <value>BBB</value>
                <value>CCC</value>
            </list>
        </property>
 
        <property name="myMap">
            <props>
                <prop key="testC">ccc</prop>
                <prop key="testD">ddd</prop>
            </props>
        </property>
 
        <property name="myProps">
            <map>
                <entry key="testA" value="aaa"></entry>
                <entry key="testB">
                    <value>BBB</value>
                </entry>
            </map>
        </property>
    </bean>
 
 
 
 
 
 
 
 
 
 
 
</beans>

AccountServiceImpl.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
package com.itheima.service.impl;
 
import com.itheima.service.IAccountService;
 
import java.util.Date;
 
/**
 * 账户的业务层实现类
 */
public class AccountServiceImpl implements IAccountService {
 
    //如果是经常变化的数据,并不适用于注入的方式
    private String name;
    private Integer age;
    private Date birthday;
 
    public AccountServiceImpl(String name,Integer age,Date birthday){
        this.name = name;
        this.age = age;
        this.birthday = birthday;
    }
 
    public void  saveAccount(){
        System.out.println("service中的saveAccount方法执行了。。。"+name+","+age+","+birthday);
    }
 
 
}

AccountServiceImpl2.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
package com.itheima.service.impl;
 
import com.itheima.service.IAccountService;
 
import java.util.Date;
 
/**
 * 账户的业务层实现类
 */
public class AccountServiceImpl2 implements IAccountService {
 
    //如果是经常变化的数据,并不适用于注入的方式
    private String name;
    private Integer age;
    private Date birthday;
 
    public void setName(String name) {
        this.name = name;
    }
 
    public void setAge(Integer age) {
        this.age = age;
    }
 
    public void setBirthday(Date birthday) {
        this.birthday = birthday;
    }
 
    public void  saveAccount(){
        System.out.println("service中的saveAccount方法执行了。。。"+name+","+age+","+birthday);
    }
 
 
}

AccountServiceImpl3.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
package com.itheima.service.impl;
 
import com.itheima.service.IAccountService;
 
import java.util.Arrays;
import java.util.List;
import java.util.Properties;
import java.util.Set;
import java.util.Map;
 
/**
 * 账户的业务层实现类
 */
public class AccountServiceImpl3 implements IAccountService {
 
    private String[] myStrs;
    private List<String> myList;
    private Set<String> mySet;
    private Map<String,String> myMap;
    private Properties myProps;
 
    public void setMyStrs(String[] myStrs) {
        this.myStrs = myStrs;
    }
 
    public void setMyList(List<String> myList) {
        this.myList = myList;
    }
 
    public void setMySet(Set<String> mySet) {
        this.mySet = mySet;
    }
 
    public void setMyMap(Map<String, String> myMap) {
        this.myMap = myMap;
    }
 
    public void setMyProps(Properties myProps) {
        this.myProps = myProps;
    }
 
    public void  saveAccount(){
        System.out.println(Arrays.toString(myStrs));
        System.out.println(myList);
        System.out.println(mySet);
        System.out.println(myMap);
        System.out.println(myProps);
    }
 
 
}

Spring创建Bean的三种方式的使用和区别

在学习Spring的时候,发现Spring的IOC(控制反转)为我们提供的三种创建Bean的方式。

1.Spring创建Bean的三种方式

这里采用XML配置,分别演示三种创建Bean的方式和代码。

先创建一个Bean   User类  三种方式都是为了得到这个User的对象

1
2
3
4
5
6
/**
 * User对象
 */
public class User {
   // 这里只是一个空对象
}

1.1 采用默认的无参构造创建实例

  XML配置:

<!– 默认的无参构建 –>

1
<bean id="user" class="ioc.pojo.User"></bean>

  测试:

1
2
3
4
5
6
7
@Test
public void testUser(){
ApplicationContext context = new ClassPathXmlApplicationContext("classpath:applicationContext.xml");
// 默认的无参构造创建
User user = (User) context.getBean("user");
System.out.println("默认的无参构造创建:" + user);
}

 控制台输出:  默认的无参构造创建:ioc.pojo.User@3b088d51

1.2 采用静态工厂创建实例

 配置工厂类:

1
2
3
4
5
6
7
8
9
10
/**
 * User对象的工厂类
 */
public class UserFactory {
// 静态方法
public static User getUser1() {
return new User();
}
  
}

 XML配置:

  <!– 使用静态工厂创建user –>

1
 <bean id="user1" class="ioc.service.UserFactory" factory-method="getUser1"></bean>

 class 指的是该工厂类的包路径,factory-method 指的是该工厂类创建Bean的静态方法。注意:这里一定要静态方法

 测试:

1
2
3
4
5
6
7
@Test
public void testUser1(){
ApplicationContext context = new ClassPathXmlApplicationContext("classpath:applicationContext.xml");
// 静态工厂创建
User user1 = (User) context.getBean("user1");
System.out.println("静态工厂创建:" + user1);
}

控制台输出结果:静态工厂创建:ioc.pojo.User@3b088d51

1.3 采用实例工厂创建实例

 配置工厂类:

1
2
3
4
5
6
7
8
9
/**
 * User对象的工厂类
 */
public class UserFactory {
//普通方法
public User getUser2() {
return new User();
}
}

XML配置:

<!– 使用实例工厂创建 user –>

1
2
    <bean id="userFactory" class="ioc.service.UserFactory"></bean>
    <bean id="user2" factory-bean="userFactory" factory-method="getUser2"></bean>

测试:

1
2
3
4
5
6
7
@Test
public void testUser2(){
ApplicationContext context = new ClassPathXmlApplicationContext("classpath:applicationContext.xml");
// 实例工厂创建
User user2 = (User) context.getBean("user2");
System.out.println("实例工厂创建:" + user2);
}

控制台输出结果:实例工厂创建:ioc.pojo.User@3b088d51

好了,实现了Spring三种Bean,感觉很顺利。

—————————————————————————————————————————————————————–

那么问题来了,为什么Spring要提供三种创建Bean的方式呢?

这三种创建Bean的方式又有什么区别呢?接下来开始做实验。

实验一: 三种方式创建的Bean是否有联系?

测试:

1
2
3
4
5
6
7
8
9
10
11
12
13
@Test
public void test1(){
ApplicationContext context = new ClassPathXmlApplicationContext("classpath:applicationContext.xml");
// 默认的无参构造创建
User user = (User) context.getBean("user");
System.out.println("默认的无参构造创建:" + user);
// 静态工厂创建
User user1 = (User) context.getBean("user1");
System.out.println("静态工厂创建:" + user1);
// 实例工厂创建
User user2 = (User) context.getBean("user2");
System.out.println("实例工厂创建:" + user2);
}

控制台输出结果:

默认的无参构造创建:ioc.pojo.User@3b088d51

静态工厂创建:ioc.pojo.User@1786dec2

实例工厂创建:ioc.pojo.User@74650e52

结论:三种方式都是创建一个新的实例对象。实例对象都是独立的,没有联系!

实验二: 三种方式创建的Bean的时机是否不同?

这里采用 bean的配置 init-method 初始化方法来查看Bean的实例是什么时候被加载的

是在加载配置文件的时候?还是在调用getBean()方法的时候?

修改User类,添加init()方法

1
2
3
4
5
6
7
8
9
/**
 * User对象
 */
public class User {
  
public void init(){
System.out.println("user被初始化啦");
}
}

XML配置:

1
2
3
4
5
6
7
8
9
    <!-- 默认的无参构建 -->
    <bean id="user" class="ioc.pojo.User" init-method="init"></bean>
  
    <!-- 使用静态工厂创建user -->
    <bean id="user1" class="ioc.service.UserFactory" factory-method="getUser1" init-method="init"></bean>
  
    <!-- 使用实例工厂创建 user -->
    <bean id="userFactory" class="ioc.service.UserFactory"></bean>
    <bean id="user2" factory-bean="userFactory" factory-method="getUser2" init-method="init"></bean>

测试:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@Test
public void test2(){
ApplicationContext context = new ClassPathXmlApplicationContext("classpath:applicationContext.xml");
System.out.println("=====================================");
// 默认的无参构造创建
User user = (User) context.getBean("user");
System.out.println("默认的无参构造创建:" + user);
// 静态工厂创建
User user1 = (User) context.getBean("user1");
System.out.println("静态工厂创建:" + user1);
// 实例工厂创建
User user2 = (User) context.getBean("user2");
System.out.println("实例工厂创建:" + user2);
}

控制台输出结果:

user被初始化啦

user被初始化啦

user被初始化啦

=====================================

默认的无参构造创建:ioc.pojo.User@3b088d51

静态工厂创建:ioc.pojo.User@1786dec2

实例工厂创建:ioc.pojo.User@74650e52

结论:从初始化方法可以看出,Spring这三种创建实例的方式都是一样的,在加载配置文件的时候就创建了实例,证明这三种方式实例加载的时机是一样的。

========================================================================================

很明显,这三种方式最根本的区别还是创建方式的不同。

第一种,通过默认的无参构造方式创建,其本质就是把类交给Spring自带的工厂(BeanFactory)管理、由Spring自带的工厂模式帮我们维护和创建这个类。如果是有参的构造方法,也可以通过XML配置传入相应的初始化参数,这种也是开发中用的最多的。

第二种,通过静态工厂创建,其本质就是把类交给我们自己的静态工厂管理,Spring只是帮我们调用了静态工厂创建实例的方法,而创建实例的这个过程是由我们自己的静态工厂实现的,在实际开发的过程中,很多时候我们需要使用到第三方jar包提供给我们的类,而这个类没有构造方法,而是通过第三方包提供的静态工厂创建的,这是时候,如果我们想把第三方jar里面的这个类交由spring来管理的话,就可以使用Spring提供的静态工厂创建实例的配置。

第三种,通过实例工厂创建,其本质就是把创建实例的工厂类交由Spring管理,同时把调用工厂类的方法创建实例的这个过程也交由Spring管理,看创建实例的这个过程也是有我们自己配置的实例工厂内部实现的。在实际开发的过程中,如Spring整合Hibernate就是通过这种方式实现的。但对于没有与Spring整合过的工厂类,我们一般都是自己用代码来管理的。

<!–把对象的创建交给spring来管理–>

    <!–spring对bean的管理细节

        1.创建bean的三种方式

        2.bean对象的作用范围

        3.bean对象的生命周期

    –>

    <!–创建Bean的三种方式 –>

    <!– 第一种方式:使用默认构造函数创建。

            在spring的配置文件中使用bean标签,配以id和class属性之后,且没有其他属性和标签时。

            采用的就是默认构造函数创建bean对象,此时如果类中没有默认构造函数,则对象无法创建。

    <bean id="accountService" class="com.itheima.service.impl.AccountServiceImpl"></bean>

    –>

    <!– 第二种方式: 使用普通工厂中的方法创建对象(使用某个类中的方法创建对象,并存入spring容器)

    <bean id="instanceFactory" class="com.itheima.factory.InstanceFactory"></bean>

    <bean id="accountService" factory-bean="instanceFactory" factory-method="getAccountService"></bean>

    –>

    <!– 第三种方式:使用工厂中的静态方法创建对象(使用某个类中的静态方法创建对象,并存入spring容器)

    <bean id="accountService" class="com.itheima.factory.StaticFactory" factory-method="getAccountService"></bean>

    –>

    <!– bean的作用范围调整

        bean标签的scope属性:

            作用:用于指定bean的作用范围

            取值: 常用的就是单例的和多例的

                singleton:单例的(默认值)

                prototype:多例的

                request:作用于web应用的请求范围

                session:作用于web应用的会话范围

                global-session:作用于集群环境的会话范围(全局会话范围),当不是集群环境时,它就是session

    <bean id="accountService" class="com.itheima.service.impl.AccountServiceImpl" scope="prototype"></bean>

    –>

    <!– bean对象的生命周期

            单例对象

                出生:当容器创建时对象出生

                活着:只要容器还在,对象一直活着

                死亡:容器销毁,对象消亡

                总结:单例对象的生命周期和容器相同

            多例对象

                出生:当我们使用对象时spring框架为我们创建

                活着:对象只要是在使用过程中就一直活着。

                死亡:当对象长时间不用,且没有别的对象引用时,由Java的垃圾回收器回收

     –>

类的解耦

通常情况下类的调用和创建都是引入 然后直接通过new的方式来操作

但是这种操作的方式会让类之间的耦合度很高 不利于代码的维护

一般简单解耦操作可以通过以下方式:

  1. 创建配置文件,内部包含需要实例化的类名

  2. 通过工厂类 使用反射的方式创建类的实例

bean.properties

1
2
accountService=cn.leokim.service.impl.AccountServiceImpl
accountDao=cn.leokim.dao.impl.AccountDaoImpl

BeanFactory

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
public class beanFactory{
    //定义一个properties对象
    provate static Properties props;
     
    //使用静态代码块为Properties对象赋值
    static{
        try{
            props = new Properties();
            //获取properties文件的流对象
            inputStream in = BeanFactory.class.getClassLoder().getResourceAsStream("bean.properties");
            porps.load(in);
        }catch(Exception e){
            throw new ExceptionInInitializerError("初始化props失败.");
        }
    }
     
    //根据Bean的名称获取bean对象
    public static Object getBean(String beanName){
        Object bean = null;
        try {
            String beanPath = props.getProperty(beanName);
//            System.out.println(beanPath);
            bean = Class.forName(beanPath).newInstance();//每次都会调用默认构造函数创建对象
        }catch (Exception e){
            e.printStackTrace();
        }
        return bean;
    }
}

如果需要单例模式可以通过以下方法在static静态代码块里将类直接实例化好,在getBean的时候直接返回实例化好的类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
import java.io.InputStream;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
 
public class BeanFactory {
    //定义一个Properties对象
    private static Properties props;
 
    //定义一个Map,用于存放我们要创建的对象。我们把它称之为容器
    private static Map<String,Object> beans;
 
    //使用静态代码块为Properties对象赋值
    static {
        try {
            //实例化对象
            props = new Properties();
            //获取properties文件的流对象
            InputStream in = BeanFactory.class.getClassLoader().getResourceAsStream("bean.properties");
            props.load(in);
            //实例化容器
            beans = new HashMap<String,Object>();
            //取出配置文件中所有的Key
            Enumeration keys = props.keys();
            //遍历枚举
            while (keys.hasMoreElements()){
                //取出每个Key
                String key = keys.nextElement().toString();
                //根据key获取value
                String beanPath = props.getProperty(key);
                //反射创建对象
                Object value = Class.forName(beanPath).newInstance();
                //把key和value存入容器中
                beans.put(key,value);
            }
        }catch(Exception e){
            throw new ExceptionInInitializerError("初始化properties失败!");
        }
    }
 
    /**
     * 根据bean的名称获取对象
     * @param beanName
     * @return
     */
    public static Object getBean(String beanName){
        return beans.get(beanName);
    }

image.png

微信小程序支付 + laravel

使用easywechat

demo:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
<?php
 
namespace App\Modules\Mall\Http\Controllers;
 
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use DB;
use App\Wxconfig;
 
/*小程序支付*/
 
class PayController extends Controller
{
    protected $pay;
    protected $gid;
    protected $wxconfig;
 
    public function __construct(Wxconfig $wxconfig, Request $request)
    {
        $this->gid = $request->input('gid');
        if ($this->gid) {
            $this->wxconfig = $wxconfig;
            $this->pay = $this->wxconfig->WxMiniPay($this->gid);
        else {
            exit('gid no exist');
        }
    }
 
    public function pay(Request $request)
    {
        $jssdk $this->pay->jssdk;
 
        $openid $request->input('openid');
        $order_no 'O' . time() . rand(10000, 99999);
 
        $order = DB::table('goods_orders')->where('order_no''O157066840848073')->first();
 
        $result $this->pay->order->unify([
            'body' => '测试小程序微信支付',
            'out_trade_no' => $order->order_no,
            'total_fee' => 1,//总价
            'notify_url' => 'http://' $_SERVER['SERVER_NAME'] . '/mall/api/payNotify',   // 支付结果通知网址,如果不设置则会使用配置里的默认地址
            'trade_type' => 'JSAPI',
            'openid' => $openid,
        ]);
 
        if ($result['return_code'] == 'SUCCESS' && $result['result_code'] == 'SUCCESS') {
            $prepayId $result['prepay_id'];
            $pay_config $jssdk->sdkConfig($prepayId);
            return $pay_config;
        else {
            return '签名错误';
        }
    }
 
    public function payNotify()
    {
        $response $this->pay->handlePaidNotify(function ($message$fail) {
            // 使用通知里的 "微信支付订单号" 或者 "商户订单号" 去自己的数据库找到订单
            $order = DB::table('goods_orders')->where('order_no'$message['out_trade_no'])->first();
 
            //订单不存在或者订单已支付
            if (!$order || $order->pay_time) {
                return true;
            }
            if ($message['return_code'] === 'SUCCESS') { // return_code 表示通信状态,不代表支付状态
                if (array_get($message'result_code') === 'SUCCESS') { // 用户是否支付成功
                    $order_data = [
                        'pay_time' => time(),
                        'pay_status' => 2,
                        'transaction_id' => $message['transaction_id'],  //微信交易号
                    ];
                    // $order->pay_time = time(); // 更新支付时间为当前时间
                    // $order->pay_status = 20;    //支付状态 成功
                    // $order->transaction_id = $message['transaction_id'];  //微信交易号
                elseif (array_get($message'result_code') === 'FAIL') {  // 用户支付失败
                    $order['pay_status'] = 3; //支付状态 失败
                }
            else {
                return $fail('通信失败,请稍后再通知我');
            }
            $order = DB::table('goods_orders')->where('order_no'$message['out_trade_no'])->update($order_data); // 保存订单
            return true; // 返回处理完成
        });
        return ['code' => 1, 'msg' => '订单支付成功!''data' => $response];
    }
 
    public function orderPayResult(Request $request)
    {
        $order_no $request->post('order_no');
        //根据商品订单号查询
        $wx_order $this->wxpay->order->queryByOutTradeNumber($order_no);
        if ($wx_order && $wx_order['trade_state'] === 'SUCCESS') {
            return ['code' => 1, 'msg' => '交易成功!'];
        }
        return ['code' => 0, 'msg' => '支付失败!''data' => $wx_order];
    }
 
 
}

小程序端

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
//支付接口
paypay: function() {
    var openid = wx.getStorageSync('openid');
    var api = this.globalData.api;
    var gid = this.globalData.gid;
    console.log(openid) wx.request({
        url: api + '/pay',
        data: {
            gid: gid,
            openid: openid,
            //order_id: order_id//订单ID
        },
        method: 'POST',
        header: {
            'content-type''application/json'
        },
        success: function(res) {
            console.log(res.data);
            wx.requestPayment({
                'timeStamp': res.data.timestamp,
                'nonceStr': res.data.nonceStr,
                'package': res.data.package,
                'signType': res.data.signType,
                'paySign': res.data.paySign,
                'success'function(res) {
                    if (res.errMsg == "requestPayment:ok") {
                        wx.showToast({
                            title: '支付成功'
                        })
                    }
                },
                'fail'function(res) {
 
}
            })
        },
        fail: function(res) {
            console.log(res.data.errmsg);
            console.log(res.data.errcode);
        },
        complete: function(res) {}
    })
},

Java 类的反射

一、什么是反射?

Java反射说的是在运行状态中,对于任何一个类,我们都能够知道这个类有哪些方法和属性。对于任何一个对象,我们都能够对它的方法和属性进行调用。我们把这种动态获取对象信息和调用对象方法的功能称之为反射机制。

二、反射的三种方式

这里需要跟大家说一下,所谓反射其实是获取类的字节码文件,也就是.class文件,那么我们就可以通过Class这个对象进行获取。

1、第一种方式

getClass

public final Class<?> getClass()

Returns the runtime class of this Object. The returned Class object is the object that is locked by static synchronized methods of the represented class.

返回类的运行实例

这个方法其实是Object的一个方法,Class继承了Object,所以我们可以直接使用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
package cn.leokim;
 
public class Reflact {
    public static void main(String[] args) {
        //创建一个对象
        Test t = new Test();
 
        //获取该对象的Class对象
        Class c = t.getClass();
 
        //获取类名称
        System.out.println(c.getName());
    }
}

2.第二种方式

1
2
3
4
5
6
7
8
9
10
package cn.leokim;
 
public class Reflact {
    public static void main(String[] args) {
        Class c = Test.class
 
        //获取类名称
        System.out.println(c.getName());
    }
}

3.第三种方式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package cn.leokim;
 
public class Reflact {
    public static void main(String[] args) {
        //这里需要注意,通过类的全路径名获取Class对象会抛出一个异常,如果根据类路径找不到这个类那么就会抛出这个异常。
        try {
            Class c = Class.forName("cn.leokim.Test");
 
            //获取类名称
            System.out.println(c.getName());
        }catch (ClassNotFoundException e){
            e.printStackTrace();
        }
    }
}

那么这3中方式我们一般选用哪种方式呢?第一种已经创建了对象,那么这个时候就不需要去进行反射了,显得有点多此一举。第二种需要导入类的包,依赖性太强。所以我们一般选中第三种方式。

三、通过反射获取类的构造方法、方法以及属性

1、获取构造方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
package cn.leokim;
 
import java.lang.reflect.Constructor;
 
public class Reflact {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException {
        //加载Class对象
        Class c = Class.forName("cn.leokim.Test");
 
        System.out.println("======================= 获取所有公用的构造方法 =======================");
        //获取所有公用的构造方法
        Constructor[] constructors = c.getConstructors();
 
        for (Constructor constructor : constructors){
            System.out.println(constructor);
        }
 
        System.out.println("======================= 获取所有公用的构造方法 =======================");
        //获取所有构造方法
        Constructor[] declareConsturctors = c.getDeclaredConstructors();
 
        for (Constructor declareConsturctor : declareConsturctors){
            System.out.println(declareConsturctor);
        }
 
        System.out.println("======================= 获取所有 公有 & 无参 的构造方法 =======================");
        Constructor constructor = c.getConstructor(null);
        System.out.println(constructor);
 
        System.out.println("======================= 获取所有 公有 & 有参 的构造方法 =======================");
        Constructor constructor1 = c.getConstructor(String.class);
        System.out.println(constructor1);
 
        System.out.println("======================= 获取所有 私有 & 有参 的构造方法 =======================");
        Constructor declareConsturctors1 = c.getDeclaredConstructor(String.class, Integer.class);
        System.out.println(declareConsturctors1);
    }
}

结果:

image.png

2、获取类属性

Test.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
package cn.leokim;
 
public class Test {
 
 
    public String getName() {
        return name;
    }
 
    public void setName(String name) {
        this.name = name;
    }
 
    public String name;
 
    public String getSex() {
        return sex;
    }
 
    public void setSex(String sex) {
        this.sex = sex;
    }
 
    private String sex;
 
    public String public_field2;
    private String private_field3;
 
 
    public Test() {
        System.out.println("no args constructor");
    }
 
    public Test(String name){
        System.out.println("Test: "+ name);
    }
 
    private Test(String name, Integer age){
        System.out.println("Name: "+ name + "Age "+ age );
    }
 
    public void t(){
        System.out.println("print t function.");
    }
 
}

Reflact.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
package cn.leokim;
 
import sun.jvm.hotspot.oops.ObjectHeap;
 
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
 
public class Reflact {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, NoSuchFieldException, IllegalAccessException, InvocationTargetException, InstantiationException {
        //加载Class对象
        Class c = Class.forName("cn.leokim.Test");
 
//        System.out.println("======================= 获取所有公用的构造方法 =======================");
//        //获取所有公用的构造方法
//        Constructor[] constructors = c.getConstructors();
//
//        for (Constructor constructor : constructors){
//            System.out.println(constructor);
//        }
//
//        System.out.println("======================= 获取所有公用的构造方法 =======================");
//        //获取所有构造方法
//        Constructor[] declareConsturctors = c.getDeclaredConstructors();
//
//        for (Constructor declareConsturctor : declareConsturctors){
//            System.out.println(declareConsturctor);
//        }
//
//        System.out.println("======================= 获取所有 公有 & 无参 的构造方法 =======================");
//        Constructor constructor = c.getConstructor(null);
//        System.out.println(constructor);
//
//        System.out.println("======================= 获取所有 公有 & 有参 的构造方法 =======================");
//        Constructor constructor1 = c.getConstructor(String.class);
//        System.out.println(constructor1);
//
//        System.out.println("======================= 获取所有 私有 & 有参 的构造方法 =======================");
//        Constructor declareConsturctors1 = c.getDeclaredConstructor(String.class, Integer.class);
//        System.out.println(declareConsturctors1);
 
 
        System.out.println("========= 获取所有的公共字段 ==========");
        Field[] fields = c.getFields();
 
        for(Field field : fields){
            System.out.println(field);
        }
 
        System.out.println("========= 获取所有的字段(public & private) ==========");
        Field[] declaredFields = c.getDeclaredFields();
 
        for(Field field : declaredFields){
            System.out.println(field);
        }
 
        System.out.println("========= 获取公有字段并使用 ==========");
        Field field = c.getField("name");
        Object obj = c.getConstructor().newInstance();
 
        //为属性设置值
        field.set(obj, "LeoKim");
        Test test = (Test) obj;
        System.out.println("Name is: " + test.getName());
 
        System.out.println("========= 获取私有字段并使用 ==========");
        Field field1 = c.getDeclaredField("sex");
        Object obj1 = c.getConstructor().newInstance();
 
        //暴力反射
        field1.setAccessible(true);
        field1.set(obj1, "男");
        Test test1 = (Test) obj1;
        System.out.println(test1.getSex());
    }
}[object Object]

这里需要注意,在获取私有属性的时候如果没有进行暴力反射,那么会抛出下面这个异常。

3.获取类中的方法

先定义几个方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public void method1(String str){
    System.out.println("public method.");
}
 
private void method2(){
    System.out.println("private method.");
}
 
String method3(String name, String sex){
    System.out.println("default method: " + name + " & " "sex");
    return name + sex;
}
 
protected  void method4(){
    System.out.println("protected method.");
}

正题:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
package cn.leokim;
 
import sun.jvm.hotspot.oops.ObjectHeap;
 
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
 
public class Reflact {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, NoSuchFieldException, IllegalAccessException, InvocationTargetException, InstantiationException, InterruptedException {
        //加载Class对象
        Class c = Class.forName("cn.leokim.Test");
 
        System.out.println("================ 获取所有的public方法 ================");
        Method[] methods = c.getMethods();
 
        for (Method method : methods){
            System.out.println(method);
        }
 
        Thread.sleep(1000);
 
        System.out.println("================ 获取所有的方法 ================");
        Method[] declaredMethods = c.getDeclaredMethods();
 
        for (Method method : declaredMethods){
            System.out.println(method);
        }
 
        Thread.sleep(1000);
         
        System.out.println("================ 获取特定方法带参并使用 ================");
        Method method1 = c.getMethod("method1", String.class);
        Object obj1 = c.getConstructor().newInstance();
        Test test1 = (Test) obj1;
        test1.method1("test str");
        Thread.sleep(1000);
 
        System.out.println("================ 获取特定方法多个参数使用 ================");
        Method method3 = c.getDeclaredMethod("method3", String.class, String.class);
        Object obj = c.getConstructor().newInstance();
        //给方法传值
        Object invoke = method3.invoke(obj, "LeoKim""男");
 
        System.out.println(invoke);
    }
}

image.png

四、反射执行main方法

1
2
3
4
5
public static void main(String[] args) {
    for (String arg : args) {
        System.out.println(arg);
    }
}
1
2
3
4
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException {
    Method method = Class.forName("cn.leokim.Test").getMethod("main", String[].class);
    method.invoke(null, (Object)new String[]{"111""222""333"});
}

Java基于接口的动态代理

所谓代理,就是不改变原有类代码的基础上,对其方法进行增强。这里以演员举例对其进行说明。演员有出演角色的行为,他们能演戏,但他们都会与经纪公司签约,而剧组找人都是找经纪公司,向公司提供一个标准。这个过程中,演员就是原生类,经纪公司就好比是在做代理这件事。经纪公司给他们公司的演员对外宣称,低于10000的戏不演,这就是在对演员的动作进行增强。代码如下:

接口:

1
2
3
4
public interface IActor {
    public void perform(int money,String name);
    public void dangerPerform(int money);
}

实现类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package com.dimples.service.impl;
  
import com.dimples.service.IActor;
  
public class MyActor implements IActor {
    @Override
    public void perform(int money,String name) {
        System.out.println("拿到" + money + "钱,执行一般表演");
    }
  
    @Override
    public void dangerPerform(int money) {
        System.out.println("拿到" + money + "钱,执行特殊表演");
    }
}

测试类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
package com.dimples.test;
  
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
  
import com.dimples.service.IActor;
import com.dimples.service.impl.MyActor;
  
public class Test2 {
  
    public static void main(String[] args) {
        //这里注意不是final类型在下面将不能被内部类引用
        final MyActor actor = new MyActor();
                //这就是在创建代理对象,第一个参数类加载器;第二个参数被代理对象实现的接口,好告知代理对象它需要具备哪些行为;第三个参数就是我们进行增强的代码
        IActor proxyActor = (IActor) Proxy.newProxyInstance(actor.getClass().getClassLoader(), actor.getClass().getInterfaces(), new InvocationHandler() {
             
            @Override
               //proxy代表代理对象,method表示执行的方法对象,args表示参数数组。被代理对象每个方法执行时都会过这个invoke方法
            public Object invoke(Object proxy, Method method, Object[] args)
                    throws Throwable {
                int money = (int) args[0];
                if("perform".equals(method.getName())){
                    if(money>10000)
                        method.invoke(actor, args);
                }else if("dangerPerform".equals(method.getName())){
                    if(money>50000)
                        method.invoke(actor, args);
                }
                return null;
            }
        });
         
        proxyActor.perform(10001"hahaha");
        proxyActor.dangerPerform(50001);
    }
     
  
}

注意,基于接口的代理方式,被代理对象至少要实现一个接口!