重學 Java 設計模式:實戰代理模式「模擬mybatis-spring中定義DAO接口,使用代理類方式操作數據庫原理實現場景」

作者:小傅哥

博客:https://bugstack.cn

沉澱、分享、成長,讓自己和他人都能有所收穫!

一、前言

難以跨越的瓶頸期,把你拿捏滴死死的!

編程開發學習過程中遇到的瓶頸期,往往是由於看不到前進的方向。這個時候你特別希望能有人告訴你,你還欠缺些什麼朝着哪個方向努力。而導致這一問題的主要原因是由於日常的業務開發太過於複製過去,日復一日的重複。沒有太多的挑戰,也沒參与過較大體量的業務場景,除了這些開發場景因素外,還有缺少組內的技術氛圍和技術分享,沒有人做傳播和佈道者,也缺少自己對各項技術學習的熱情,從而導致一直遊盪在瓶頸之下,難以提升。

小公司與大公司,選擇哪個?

刨除掉薪資以外你會選擇什麼,是不有人建議小公司,因為可以接觸到各個環境,也有人建議大公司,因為正規體量大可以學習到更多。有些時候你的技術成長緩慢也是因為你的不同選擇而導致的,小公司確實要接觸各個環境,但往往如果你所做的業務體量不高,那麼你會用到的技術棧就會相對較少,同時也會技術棧研究的深度也會較淺。大公司中確實有時候你不需要去關心一個集群的部署和維護、一个中間件的開發、全套服務監控等等,但如果你願意了解這些技術在內部都是公開的,你會擁有無限的技術營養可以補充。而這最主要的是提升視野和事業。

除了業務中的CRUD開發,有些技術你真的很難接觸到!

可能很多小夥伴認為技術開發就是承接下產品需求,寫寫CRUD,不會的百度一下,就完事了,總覺得別人問的東西像再造火箭一樣。但在高體量、高併發的業務場景下,每一次的壓測優化,性能提升,都像在研究一道數學題一樣,反覆的錘鍊,壓榨性能。不斷的深究,找到最合適的設計。除了這些優化提升外,還有那麼廣闊的技術體系棧,都可能因為你只是注重CRUD而被忽略;字節碼編程、領域驅動設計架構、代理模式中間件開發、JVM虛擬機實現原理等等。

二、開發環境

  1. JDK 1.8
  2. Idea + Maven
  3. Spring 4.3.24.RELEASE
  4. 涉及工程三個,可以通過關注公眾號:bugstack蟲洞棧,回復源碼下載獲取(打開獲取的鏈接,找到序號18)
工程 描述
itstack-demo-design-12-00 模擬MyBatis開發中間件代理類部分

三、代理模式介紹

代理模式有點像老大和小弟,也有點像分銷商。主要解決的是問題是為某些資源的訪問、對象的類的易用操作上提供方便使用的代理服務。而這種設計思想的模式經常會出現在我們的系統中,或者你用到過的組件中,它們都提供給你一種非常簡單易用的方式控制原本你需要編寫很多代碼的進行使用的服務類。

類似這樣的場景可以想到;

  1. 你的數據庫訪問層面經常會提供一個較為基礎的應用,以此來減少應用服務擴容時不至於數據庫連接數暴增。
  2. 使用過的一些中間件例如;RPC框架,在拿到jar包對接口的描述后,中間件會在服務啟動的時候生成對應的代理類,當調用接口的時候,實際是通過代理類發出的socket信息進行通過。
  3. 另外像我們常用的MyBatis,基本是定義接口但是不需要寫實現類,就可以對xml或者自定義註解里的sql語句進行增刪改查操作。

四、案例場景模擬

在本案例中我們模擬實現mybatis-spring中代理類生成部分

對於Mybatis的使用中只需要定義接口不需要寫實現類就可以完成增刪改查操作,有疑問的小夥伴,在本章節中就可以學習到這部分知識。解析下來我們會通過實現一個這樣的代理類交給spring管理的核心過程,來講述代理類模式。

這樣的案例場景在實際的業務開發中其實不多,因為這是將這種思想運用在中間件開發上,而很多小夥伴經常是做業務開發,所以對Spring的bean定義以及註冊和對代理以及反射調用的知識了解的相對較少。但可以通過本章節作為一個入門學習,逐步了解。

五、代理類模式實現過程

接下來會使用代理類模式來模擬實現一個Mybatis中對類的代理過程,也就是只需要定義接口,就可以關聯到方法註解中的sql語句完成對數據庫的操作。

這裏需要注意一些知識點;

  1. BeanDefinitionRegistryPostProcessor,spring的接口類用於處理對bean的定義註冊。
  2. GenericBeanDefinition,定義bean的信息,在mybatis-spring中使用到的是;ScannedGenericBeanDefinition 略有不同。
  3. FactoryBean,用於處理bean工廠的類,這個類非常見。

1. 工程結構

itstack-demo-design-12-00
└── src
    ├── main
    │   ├── java
    │   │   └── org.itstack.demo.design
    │   │       ├── agent
    │   │       │	├── MapperFactoryBean.java
    │   │       │	├── RegisterBeanFactory.java
    │   │       │	└── Select.java
    │   │       └── IUserDao.java
    │   └── resources	
    │       └── spring-config.xml
    └── test
        └── java
            └── org.itstack.demo.test
                └── ApiTest.java

代理模式中間件模型結構

  • 此模型中涉及的類並不多,但都是抽離出來的核心處理類。主要的事情就是對類的代理和註冊到spring中。
  • 上圖中最上面是關於中間件的實現部分,下面對應的是功能的使用。

2. 代碼實現

2.1 自定義註解

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface Select {

    String value() default "";  // sql語句

}
  • 這裏我們定義了一個模擬mybatis-spring中的自定義註解,用於使用在方法層面。

2.2 Dao層接口

public interface IUserDao {

    @Select("select userName from user where id = #{uId}")
    String queryUserInfo(String uId);

}
  • 這裏定義一個Dao層接口,並把自定義註解添加上。這與你使用的mybatis組件是一樣的。
  • 2.1和2.2是我們的準備工作,後面開始實現中間件功能部分。

2.3 代理類定義

public class MapperFactoryBean<T> implements FactoryBean<T> {

    private Logger logger = LoggerFactory.getLogger(MapperFactoryBean.class);

    private Class<T> mapperInterface;

    public MapperFactoryBean(Class<T> mapperInterface) {
        this.mapperInterface = mapperInterface;
    }

    @Override
    public T getObject() throws Exception {
        InvocationHandler handler = (proxy, method, args) -> {
            Select select = method.getAnnotation(Select.class);
            logger.info("SQL:{}", select.value().replace("#{uId}", args[0].toString()));
            return args[0] + ",小傅哥,bugstack.cn - 沉澱、分享、成長,讓自己和他人都能有所收穫!";
        };
        return (T) Proxy.newProxyInstance(this.getClass().getClassLoader(), new Class[]{mapperInterface}, handler);
    }

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

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

}
  • 如果你有閱讀過mybatis源碼,是可以看到這樣的一個類;MapperFactoryBean,這裏我們也模擬一個這樣的類,在裏面實現我們對代理類的定義。
  • 通過繼承FactoryBean,提供bean對象,也就是方法;T getObject()。
  • 在方法getObject()中提供類的代理以及模擬對sql語句的處理,這裏包含了用戶調用dao層方法時候的處理邏輯。
  • 還有最上面我們提供構造函數來透傳需要被代理類,Class<T> mapperInterface,在mybatis中也是使用這樣的方式進行透傳。
  • 另外getObjectType()提供對象類型反饋,以及isSingleton()返回類是單例的。

2.4 將Bean定義註冊到Spring容器

public class RegisterBeanFactory implements BeanDefinitionRegistryPostProcessor {
    
    @Override
    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
        
        GenericBeanDefinition beanDefinition = new GenericBeanDefinition();
        beanDefinition.setBeanClass(MapperFactoryBean.class);
        beanDefinition.setScope("singleton");
        beanDefinition.getConstructorArgumentValues().addGenericArgumentValue(IUserDao.class);

        BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(beanDefinition, "userDao");
        BeanDefinitionReaderUtils.registerBeanDefinition(definitionHolder, registry);
    }

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {
        // left intentionally blank
    }

}
  • 這裏我們將代理的bean交給spring容器管理,也就可以非常方便讓我們可以獲取到代理的bean。這部分是spring中關於一個bean註冊過程的源碼。
  • GenericBeanDefinition,用於定義一個bean的基本信息setBeanClass(MapperFactoryBean.class);,也包括可以透傳給構造函數信息addGenericArgumentValue(IUserDao.class);
  • 最後使用 BeanDefinitionReaderUtils.registerBeanDefinition,進行bean的註冊,也就是註冊到DefaultListableBeanFactory中。

2.5 配置文件spring-config

<?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-3.0.xsd"
       default-autowire="byName">

    <bean id="userDao" class="org.itstack.demo.design.agent.RegisterBeanFactory"/>

</beans>
  • 接下來在配置文件中添加我們的bean配置,在mybatis的使用中一般會配置掃描的dao層包,這樣就可以減少這部分的配置。

3. 測試驗證

3.1 編寫測試類

@Test
public void test_IUserDao() {
    BeanFactory beanFactory = new ClassPathXmlApplicationContext("spring-config.xml");
    IUserDao userDao = beanFactory.getBean("userDao", IUserDao.class);
    String res = userDao.queryUserInfo("100001");
    logger.info("測試結果:{}", res);
}
  • 測試的過程比較簡單,通過加載Bean工廠獲取我們的代理類的實例對象,之後調用方法返回結果。
  • 那麼這個過程你可以看到我們是沒有對接口先一個實現類的,而是使用代理的方式給接口生成一個實現類,並交給spring管理。

3.2 測試結果

23:21:57.551 [main] DEBUG o.s.core.env.StandardEnvironment - Adding PropertySource 'systemProperties' with lowest search precedence
...
23:21:57.858 [main] DEBUG o.s.c.s.ClassPathXmlApplicationContext - Unable to locate LifecycleProcessor with name 'lifecycleProcessor': using default [org.springframework.context.support.DefaultLifecycleProcessor@7bc1a03d]
23:21:57.859 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Returning cached instance of singleton bean 'lifecycleProcessor'
23:21:57.860 [main] DEBUG o.s.c.e.PropertySourcesPropertyResolver - Could not find key 'spring.liveBeansView.mbeanDomain' in any property source
23:21:57.861 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Returning cached instance of singleton bean 'userDao'
23:21:57.915 [main] INFO  o.i.d.design.agent.MapperFactoryBean - SQL:select userName from user where id = 100001
23:21:57.915 [main] INFO  org.itstack.demo.design.test.ApiTest - 測試結果:100001,小傅哥,bugstack.cn - 沉澱、分享、成長,讓自己和他人都能有所收穫!

Process finished with exit code 0
  • 從測試結果可以看到,我們打印了SQL語句,這部分語句是從自定義註解中獲取的;select userName from user where id = 100001,我們做了簡單的適配。在mybatis框架中會交給SqlSession的實現類進行邏輯處理返回操作數據庫數據
  • 而這裏我們的測試結果是一個固定的,如果你願意更加深入的研究可以嘗試與數據庫操作層進行關聯,讓這個框架可以更加完善。

六、總結

  • 關於這部分代理模式的講解我們採用了開發一個關於mybatis-spring中間件中部分核心功能來體現代理模式的強大之處,所以涉及到了一些關於代理類的創建以及spring中bean的註冊這些知識點,可能在平常的業務開發中都是很少用到的,但是在中間件開發中確實非常常見的操作。
  • 代理模式除了開發中間件外還可以是對服務的包裝,物聯網組件等等,讓複雜的各項服務變為輕量級調用、緩存使用。你可以理解為你家裡的電燈開關,我們不能操作220v電線的人肉連接,但是可以使用開關,避免觸電。
  • 代理模式的設計方式可以讓代碼更加整潔、乾淨易於維護,雖然在這部分開發中額外增加了很多類也包括了自己處理bean的註冊等,但是這樣的中間件復用性極高也更加智能,可以非常方便的擴展到各個服務應用中。

七、推薦閱讀

  • 1. 重學 Java 設計模式:實戰工廠方法模式(多種類型商品發獎場景)
  • 2. 重學 Java 設計模式:實戰建造者模式(裝修物料組合套餐選配場景)
  • 3. 重學 Java 設計模式:實戰原型模式(多套試每人題目和答案亂序場景)
  • 4. 重學 Java 設計模式:實戰橋接模式(多支付渠道「微信支付寶」與多支付模式「刷臉、指紋」場景)
  • 5. 重學 Java 設計模式:實戰組合模式(營銷差異化人群發券決策樹引擎搭建場景)
  • 6. 重學 Java 設計模式:實戰外觀模式「基於SpringBoot開發門面模式中間件,統一控制接口白名單場景」

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※網頁設計公司推薦不同的風格,搶佔消費者視覺第一線

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

※自行創業缺乏曝光? 網頁設計幫您第一時間規劃公司的形象門面

※南投搬家公司費用需注意的眉眉角角,別等搬了再說!

※教你寫出一流的銷售文案?

Spring系列.AOP原理簡析

Spring AOP使用簡介

Spring的兩大核心功能是IOC和AOP。當我們使用Spring的AOP功能時是很方便的。只需要進行下面的配置即可。

@Component
@Aspect
public class MyAspect {

//PointCut匹配的方法必須是Spring中bean的方法
//Pointcut可以有下列方式來定義或者通過&& || 和!的方式進行組合.
//下面定義的這些切入點就可以通過&& ||組合

private static Logger logger = LoggerFactory.getLogger(MyAspect.class);

//*:代表方法的返回值可以是任何類型
//整個表達式匹配controller包下面任何的的echo方法,方法入參樂意是任意
@Pointcut("execution(* com.csx.demo.spring.boot.controller.*.echo(..))")
public void pointCut1(){}

@Before("pointCut1()")
public void befor(){
    logger.info("前置通知vvvv...");
    logger.info("我要做些事情...");
}
}

然後再開啟註解

//自動選擇合適的AOP代理
//傳統xml這樣配置:<aop:aspectj-autoproxy/>

//exposeProxy = true屬性設置成true,意思是將動態生成的代理類expose到AopContext的ThreadLocal線程
//可以通過AopContext.currentProxy();獲取到生成的動態代理類。

//proxyTargetClass屬性設置動態代理使用JDK動態代理還是使用CGlib代理,設置成true是使用CGlib代理,false的話是使用JDK動態代理

//注意:如果使用Spring Boot的話,下面的配置可以不需要。AopAutoConfiguration這個自動配置類中已經自動開啟了AOP
//默認使用CGLIB動態代理,Spring Boot配置的優先級高於下面的配置

@Configuration
@EnableAspectJAutoProxy(exposeProxy = true,proxyTargetClass = false)
public class AopConfig {

}

通上面的配置,當我們調用controller包下面的任何類的echo方法時就會觸發前置通知。其實這個說法不是很準確。因為我們調用的類已經不是我們自己寫的類了。而是Spring框架通過動態代理生成的類。

稍微了解一點Spring AOP的同學都會知道Spring的AOP是通過動態代理實現的。那Spring是怎麼生成動態代理類,並將Advice織入代理類的呢?整個流程是怎樣的呢?下面就分析下Spring生成動態代理類的過程。

需要說明下的是,本博客旨在梳理整個AOP動態代理的過程,細節方面需要大家自己去看。

@EnableAspectJAutoProxy幹了些啥

如果讓你從頭開始研究下AOP的原理,你是不是一頭霧水,根本不知道從何入手。但其實看Spring的代碼有個小技巧:如果你要研究一個功能,可以從開啟這個功能的Enable註解開始看。Spring的很多功能都是通過Enable註解開啟的,所以這些註解肯定和這些功能相關。

那麼這邊我們可以從@EnableAspectJAutoProxy這個註解開始着手,看下這個註解做了些什麼操作。

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(AspectJAutoProxyRegistrar.class)
public @interface EnableAspectJAutoProxy {
    //設置為true的話就一直使用cglib動態代理
    //設置為false的話,對於接口使用jdk動態代理,對於類使用cglib代理
	boolean proxyTargetClass() default false;
	boolean exposeProxy() default false;
}

看到上面的@Impoer註解,我們很自然就會想到去看AspectJAutoProxyRegistrar這個類。

//AspectJAutoProxyRegistrar源代碼
class AspectJAutoProxyRegistrar implements ImportBeanDefinitionRegistrar {

    /**
     * 主要作用也就是註冊AnnotationAwareAspectJAutoProxyCreator
     */
    @Override
    public void registerBeanDefinitions(
            AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
        //註冊AnnotationAwareAspectJAutoProxyCreator的BeanDefinition
        AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry);

        AnnotationAttributes enableAspectJAutoProxy =
                AnnotationConfigUtils.attributesFor(importingClassMetadata, EnableAspectJAutoProxy.class);
        if (enableAspectJAutoProxy != null) {
            //給上面註冊的BeanDefinition中添加兩個屬相proxyTargetClass和exposeProxy
            if (enableAspectJAutoProxy.getBoolean("proxyTargetClass")) {
                AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
            }
            if (enableAspectJAutoProxy.getBoolean("exposeProxy")) {
                AopConfigUtils.forceAutoProxyCreatorToExposeProxy(registry);
            }
        }
    }

}

我們可以看到上面的類中也沒幹什麼特別的事情,就註冊了一個BeanDefinition。如果我們點進去看下AnnotationAwareAspectJAutoProxyCreator這個類的源代碼會發現這個類竟然實現了InstantiationAwareBeanPostProcessor這個接口。熟悉Spring尿性的朋友會敏銳的感覺到Spring可能是在postProcessBeforeInstantiation或者postProcessAfterInstantiation這些方法中對Bean進行動態代理的。

“大膽假設,小心求證”,讓我們帶着這個猜想去看看AnnotationAwareAspectJAutoProxyCreator到底幹了些什麼?

AnnotationAwareAspectJAutoProxyCreator生成動態代理類

@Override
public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) {
    Object cacheKey = getCacheKey(beanClass, beanName);

    if (!StringUtils.hasLength(beanName) || !this.targetSourcedBeans.contains(beanName)) {
        if (this.advisedBeans.containsKey(cacheKey)) {
            return null;
        }
        if (isInfrastructureClass(beanClass) || shouldSkip(beanClass, beanName)) {
            this.advisedBeans.put(cacheKey, Boolean.FALSE);
            return null;
        }
    }
    // 一般不指定CustomTargetSource,所以不會進入這段代碼,所以關鍵代碼在
    // postProcessAfterInitialization中
    // Create proxy here if we have a custom TargetSource.
    // Suppresses unnecessary default instantiation of the target bean:
    // The TargetSource will handle target instances in a custom fashion.
    TargetSource targetSource = getCustomTargetSource(beanClass, beanName);
    if (targetSource != null) {
        if (StringUtils.hasLength(beanName)) {
            this.targetSourcedBeans.add(beanName);
        }
        Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(beanClass, beanName, targetSource);
        Object proxy = createProxy(beanClass, beanName, specificInterceptors, targetSource);
        this.proxyTypes.put(cacheKey, proxy.getClass());
        return proxy;
    }
    return null;
}

下面是創建動態代理類的關鍵代碼。

@Override
    public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {
        if (bean != null) {
            Object cacheKey = getCacheKey(bean.getClass(), beanName);
            if (this.earlyProxyReferences.remove(cacheKey) != bean) {
                //這邊是創建代碼類的關鍵代碼
                return wrapIfNecessary(bean, beanName, cacheKey);
            }
        }
        return bean;
    }
protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
        if (StringUtils.hasLength(beanName) && this.targetSourcedBeans.contains(beanName)) {
            return bean;
        }
        if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {
            return bean;
        }
        if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {
            this.advisedBeans.put(cacheKey, Boolean.FALSE);
            return bean;
        }

        // Create proxy if we have advice.
        //獲取當前Bean配置的advice,這步是關鍵
        Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
        if (specificInterceptors != DO_NOT_PROXY) {
            this.advisedBeans.put(cacheKey, Boolean.TRUE);
            //創建代理
            Object proxy = createProxy(
                    bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
            this.proxyTypes.put(cacheKey, proxy.getClass());
            return proxy;
        }

        this.advisedBeans.put(cacheKey, Boolean.FALSE);
        return bean;
    }

層層dedug進去我們能看到下面這段代碼,我們口中常說的JDK動態代理和Cglib動態代理就是在這邊生成的。

public class DefaultAopProxyFactory implements AopProxyFactory, Serializable {

    @Override
    public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
        if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) {
            Class<?> targetClass = config.getTargetClass();
            if (targetClass == null) {
                throw new AopConfigException("TargetSource cannot determine target class: " +
                        "Either an interface or a target is required for proxy creation.");
            }
            if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
                //生成JDK動態代理
                return new JdkDynamicAopProxy(config);
            }
            //生成Cglib動態代理
            return new ObjenesisCglibAopProxy(config);
        }
        else {
            return new JdkDynamicAopProxy(config);
        }
    }
    /**
     * Determine whether the supplied {@link AdvisedSupport} has only the
     * {@link org.springframework.aop.SpringProxy} interface specified
     * (or no proxy interfaces specified at all).
     */
    private boolean hasNoUserSuppliedProxyInterfaces(AdvisedSupport config) {
        Class<?>[] ifcs = config.getProxiedInterfaces();
        return (ifcs.length == 0 || (ifcs.length == 1 && SpringProxy.class.isAssignableFrom(ifcs[0])));
    }

到此,我們已經簡單分析了Spring動態代理類的生成流程。

PS:關於InstantiationAwareBeanPostProcessor接口和BeanPostProcessor接口大家可以自行了解下,這兩個接口是Spring中非常重要的接口。看懂了這兩個接口,Spring很多“神秘”的功能你就能理解了。

簡單總結

通過上面分析,其實我們發現如果不去看AOP動態代理類生成的細節的話,整個Spring AOP的流程還是挺簡單的:

  • @EnableAspectJAutoProxy註解通過AopConfigUtils這個工具類註冊AnnotationAwareAspectJAutoProxyCreator這個類,這個類實現了InstantiationAwareBeanPostProcessor接口,所以會在Bean實例化前後對Bean做一系列額外的操作;
  • AnnotationAwareAspectJAutoProxyCreator的postProcessAfterInitialization中會找出所有和當前Bean相關的Advice,如果找到就創建相應的動態代理類,如果找不到就不生成,返回原始類。

所以整個大流程就這麼簡單。

一些重要類:

  • @EnableAspectJAutoProxy;
  • AspectJAutoProxyRegistrar:註冊AnnotationAwareAspectJAutoProxyCreator
  • AnnotationAwareAspectJAutoProxyCreator:AOP動態代理自動生成的處理類,其他類似的類有AspectJAwareAdvisorAutoProxyCreator和InfrastructureAdvisorAutoProxyCreator等;
  • AopConfigUtils:AOP配置工具類
  • ProxyFactory:代理工廠
  • AopProxy接口:常見實現類ObjenesisCglibAopProxy、JdkDynamicAopProxy

參考

  • https://blog.csdn.net/zhoushimiao1990/article/details/89853368

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※自行創業缺乏曝光? 網頁設計幫您第一時間規劃公司的形象門面

※網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

※想知道最厲害的網頁設計公司"嚨底家"!

※別再煩惱如何寫文案,掌握八大原則!

※產品缺大量曝光嗎?你需要的是一流包裝設計!

加州電動車市場吹風向?純電動車超越插電式油電車

加州因為諸多有利電動車發展的政策,成為全球電動車的領先市場,其市場走向動見觀瞻,過去電動車市場曾經以插電式油電混合車為主,純電動車占較少比例,但如今加州市場率先反轉,純電動車與插電式油電混合車出現翻轉,這也代表全球電動車市場可能將開始往純電動車傾斜。

加州的總體電動車市場持續成長,2016 年純電動車加上插電式油電混合車,總市佔為 3.6%,2017 年提升到 4.8%,到 2018 年上半年,再提升到 6.2%,雖然占總體市場仍低,但市佔比例增加的幅度相當可觀。在分項部分,2014 年時,純電動車在加州市場已經追上差距,與插電式油電混合車已經達到市佔比例相當,如今純電動車更勝一籌,2018 上半年,純電動車在加州市佔 3.3%,超越插電式油電混合車的 2.9%。

不僅電動車追上插電式油電混合車,兩者的快速成長,也追上了傳統油電混合車,傳統油電混合車並不被視為電動車,在過去,傳統油電混合車的市佔遠高於電動車與插電式油電混合車,2014 年時傳統油電混合車市佔 6.3%,當年插電式油電混合車市佔僅 1.6%,純電動車也僅占 1.6%,顯示消費者對於加油還是比充電更有信心。

然而近年來趨勢有反轉的傾向,傳統油電混合車市佔節節下滑,2015 年落到 5.8%,2016 年再跌到 4.7%,2017 年 4.6%,2018 年上半年則約為 4%;相對的,插電式油電混合車與電動車則蒸蒸日上,2015 年兩者分別為 1.4%、1.7%,2016 年 1.7%、1.9%,2017 年 2.2%、2.6%,到 2018 年上半年的 2.9%、3.3%。

總體來說,可說插電式油電混合車取代了傳統油電混合車,在此同時,純電動車則成長還超前插電式油電混合車。過去車廠對於電動車的演變看法不同,有車廠認為會先經歷傳統油電混合車成為主流的年代,之後再過渡到插電式油電混合車為主流,最後才進入純電動車時代,但加州經驗看來,3 種車都還未成為主流,傳統油電混合車就已經節節下滑,將下台一鞠躬,插電式油電混合車雖然崛起,但純電動車更勝一籌,由此觀之,或許未來的車市發展,還是會直接跳到純電動車,而沒有所謂油電混合車時代。

(合作媒體:。首圖來源:)

本站聲明:網站內容來源於EnergyTrend https://www.energytrend.com.tw/ev/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※帶您來了解什麼是 USB CONNECTOR  ?

※自行創業缺乏曝光? 網頁設計幫您第一時間規劃公司的形象門面

※如何讓商品強力曝光呢? 網頁設計公司幫您建置最吸引人的網站,提高曝光率!

※綠能、環保無空污,成為電動車最新代名詞,目前市場使用率逐漸普及化

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

※教你寫出一流的銷售文案?

美環保組織:燕麥片含致癌除草劑 美國桂格、家樂氏上榜

摘錄自2018年08月18日蘋果日報美國報導

美國環保組織「環境工作小組」(Environmental Working Group,EWG)近日選取45種燕麥食物送化驗,結果發現其中43種含有除草劑「年年春」(Roundup)的主要化學物質嘉磷塞(glyphosate),且超過2/3產品的嘉磷塞含量,超出兒童攝取的安全份量。而讓人驚訝的是,美國的桂格燕麥(Quaker Oats)、家樂氏(Kellogg’s)和Cheerios等的化學物含量最高。對此,美國桂格燕麥及家樂氏均強調,旗下食品合乎安全標準。

EWG指,兒童對嘉磷塞的安全攝取量,不應超過160 ppb,但桂格燕麥兩個樣本,均被驗出嘉磷塞的濃度超過1000 ppb。此外,桂格的另外兩種產品「恐龍蛋即食燕麥片」(Quaker’s dinosaur eggs instant oatmeal)和「鋼切燕麥粒」(Steel Cut Oats),嘉磷塞含量也高達500 ppb。

本站聲明:網站內容來源環境資訊中心https://e-info.org.tw/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※為什麼 USB CONNECTOR 是電子產業重要的元件?

※網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

※台北網頁設計公司全省服務真心推薦

※想知道最厲害的網頁設計公司"嚨底家"!

※推薦評價好的iphone維修中心

警訊! 氣候暖化 美病媒蚊疾病13年增兩倍

環境資訊中心外電;姜唯 翻譯;林大利 審校;稿源:ENS

天氣越來越熱,美國各大城市的蚊蟲也變多了。美國政府統計,在過去13年(2004-2016)間,蚊子、蜱蟲和跳蚤傳染病增加了兩倍,出現超過64萬2600例病例。根據美國疾病預防管制中心(CDC)最新的《生命徵象》(vital signs)報告,同一時期由蚊子和蜱蟲引入的傳播新病原體就多達九種。

埃及斑蚊,
Aedes aegypti。圖片來源:美國農業部 USDA-ARS

「茲卡、西尼羅熱、萊姆病和屈公熱,近年蚊子、蜱蟲或跳蚤傳染病越來越多,我們不知道接下來還有什麼病會進入美國。」CDC主任瑞德菲爾醫師(Robert Redfield)說。

此外,紐澤西倡議團體氣候中心(Climate Central)研究發現,隨著氣溫上升,全美大部分地區病媒蚊活躍期逐漸延長,蚊子傳播疾病的風險更大。為了研究溫度對蚊子傳播疾病的作用,該中心分析了各年春季、夏季和秋季平均溫度介於16.1°C(61°F)和33.9°C(93°F)之間的天數。這是黑斑蚊傳染疾病的溫度範圍。

244個城市中,有94%的城市氣溫介於16.1°C和33.9°C的天數增加,顯示疾病傳播的風險增加,即病媒蚊活躍期。

埃及斑紋和白線斑蚊兩種黑斑蚊都會傳播西尼羅熱。自1999年進入美國後,48州都有病例。黑斑蚊還會傳染其他危險疾病,如登革熱、茲卡、屈公熱和黃熱病。

要傳播疾病,蚊子必須叮咬兩次,第一次被傳染,第二次傳染給別人。華氏75度時,發生兩次叮咬的蚊子數量最多。

病媒蚊活躍期最長的前10名城市中,加州佔了三名,其他城市包括新墨西哥州拉斯克魯塞斯、德州埃爾帕索市和亞利桑那州圖森市。這些城市的氣溫都很適合病媒蚊生存跟傳染疾病。

夏天前往美國阿拉斯加森林得必備防蚊頭套。圖片來源: (CC BY-NC 2.0)

氣候中心的分析顯示,自20世紀70年代以來,由於氣溫升高,這些城市的疾病傳播風險一直在增加。部分北方城市則是活躍日延長幅度最大,包括蒙大拿州海倫娜和賓州伊利市。

氣候變遷也可能使某些地方溫度過高,以至於不適合病媒蚊生存。例如,1970年以來鳳凰城的病媒蚊活躍日數減少,因為當地35°C(95°F)以上的天數也增加了,而這已經超出傳染範圍。

244個城市中,只有12個城市疾病活躍天數減少。

Mosquito ‘Danger Days’ Multiply as Climate Warms PRINCETON, New Jersey, August 14, 2018 (ENS)

Sicknesses from mosquito, tick, and flea bites have tripled in the United States, with just over 642,600 cases reported during the 13 years from 2004 through 2016. Nine new germs spread by mosquitoes and ticks were discovered or introduced into the United States during this time, according to the latest “Vital Signs” report by the Centers for Disease Control and Prevention, CDC.

“Zika, West Nile, Lyme, and chikungunya – a growing list of diseases caused by the bite of an infected mosquito, tick, or flea – have confronted the U.S. in recent years, making a lot of people sick. And we don’t know what will threaten Americans next,” said CDC Director Robert Redfield, M.D

In addition, the number of mosquito “disease danger days” is increasing across much of the country as temperatures rise, representing a greater risk for transmission of mosquito-borne diseases, finds new research by the New Jersey-based science and advocacy group Climate Central.

To examine the role temperature is playing in disease transmission from mosquitoes, Climate Central analyzed the number of days each year in the spring, summer, and fall with an average temperature between 61 degrees and 93 degrees Fahrenheit. This is the range for transmission of diseases spread by mosquitoes of the Aedes type.

Of the 244 cities analyzed, 94 percent are seeing an increase in the number of days with temps between 61 and 93, indicating a heightened risk for disease transmission, or “disease danger days.”

Both types of mosquitoes transmit West Nile virus, with cases in all 48 continental states since its introduction to the United States in 1999. Aedes mosquitoes carry other dangerous diseases such as dengue, Zika, chikungunya, and yellow fever.

In order to transmit disease, a mosquito must bite twice – once to acquire the disease and a second time to pass it on. The largest number of these twice-biting mosquitoes are produced at 75 degrees Fahrenheit.

Three California cities rank numbers two, three, and nine on the list of biggest increases in disease danger days. Other cities in the top 10 list include southern and southwestern cities like Las Cruces, New Mexico, El Paso, Texas and Tucson, Arizona, where temperatures are suitable for mosquito survival and some disease transmission already occurs.

The Climate Central analysis suggests that since the 1970s, the risk of disease transmission in these cities has been increasing due to rising temperatures.

A few northern cities also make this list of greatest increases in disease danger days, including Helena, Montana, and Erie, Pennsylvania. 

Climate change may make some locations too hot for mosquito survival and disease transmission. For example, there are fewer disease danger days in Phoenix since 1970. This is likely because the number of days above 95 degrees Fahrenheit in Phoenix is also going up, and 95 degrees is beyond the range of disease transmission.

Out of the 244 cities in the analysis, only 12 are seeing a decrease in the number of disease danger days during this time period.

※ 全文及圖片詳見:

作者

如果有一件事是重要的,如果能為孩子實現一個願望,那就是人類與大自然和諧共存。

於特有生物研究保育中心服務,小鳥和棲地是主要的研究對象。是龜毛的讀者,認為龜毛是探索世界的美德。

延伸閱讀

本站聲明:網站內容來源環境資訊中心https://e-info.org.tw/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

※網頁設計公司推薦不同的風格,搶佔消費者視覺第一線

※想知道購買電動車哪裡補助最多?台中電動車補助資訊懶人包彙整

※南投搬家公司費用,距離,噸數怎麼算?達人教你簡易估價知識!

※教你寫出一流的銷售文案?

※超省錢租車方案

全球首創 東京用碳交易替大型建築減碳 六年減下1/4

環境資訊中心記者 陳文姿報導

本站聲明:網站內容來源環境資訊中心https://e-info.org.tw/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

※網頁設計公司推薦不同的風格,搶佔消費者視覺第一線

※Google地圖已可更新顯示潭子電動車充電站設置地點!!

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

※別再煩惱如何寫文案,掌握八大原則!

※網頁設計最專業,超強功能平台可客製化

美稱節約石油無經濟必要性 環保署:人們根本不想買電動車

摘錄自2018年8月20日自由時報報導

美國川普政府近期在重大新政策聲明中宣告,節約石油對美國已無經濟必要性,這恐將破壞美國政府數十年來推動省油車和其他節約計劃的舉動。美國環保署官員韋魯姆(Bill Wehrum)更對電動車不屑一顧,直言人們根本就不想買電動車。

《美聯社》報導,川普政府在上月公布1份支持政府放寬里程油耗標準的備忘錄上,表達了上述立場。

備忘錄正挑戰著過去節省石油的理由,甚至是國會規定,但備忘錄中並沒有提及氣候變化等相關問題。運輸是引發氣候變遷氣體最大的單一排放源。

值得一提的是,美國環保署空氣與輻射辦公室助理行政長官韋魯姆對電動車不屑一顧,直言人們根本不會想買電動車。

本站聲明:網站內容來源環境資訊中心https://e-info.org.tw/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※網頁設計公司推薦不同的風格,搶佔消費者視覺第一線

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

※自行創業缺乏曝光? 網頁設計幫您第一時間規劃公司的形象門面

※南投搬家公司費用需注意的眉眉角角,別等搬了再說!

※教你寫出一流的銷售文案?

福島核電廠淨化核污水 檢出半衰期1570萬年超標碘129

摘錄自2018年8月20日中央社報導

日本媒體報導,2011年發生核子事故的福島第一核電廠所淨化的核污水,除了氚以外,還被檢測出其他放射性物質,包括半衰期長達1570萬年的碘129。

共同社報導,福島縣知事內堀雅雄今天(20日)表示,有關這個問題,希望中央政府與營運福島第一核電廠的東京電力公司,能仔細說明水質狀況數據,及對環境與當地形象會造成什麼影響。

東電公司表示,2017年度使用放射性廢水處理系統(ALPS)淨化含氚水後的檢測結果顯示,半衰期約1570萬年的碘129,每公升最多檢出62.2貝克,比標準值多9貝克。

關於如何處理持續聚積在福島第一核電廠內的含氚水,基於對人體影響較小等原則,日本政府已設置小委員會全面展開討論,月底也將舉行傾聽國民意見的聽證會;但關於氚以外的放射性物質則幾乎沒被討論。

本站聲明:網站內容來源環境資訊中心https://e-info.org.tw/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※自行創業缺乏曝光? 網頁設計幫您第一時間規劃公司的形象門面

※網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

※想知道最厲害的網頁設計公司"嚨底家"!

※別再煩惱如何寫文案,掌握八大原則!

※產品缺大量曝光嗎?你需要的是一流包裝設計!

特斯拉上海子公司台幣 44 億元取得上海土地,超級工廠正式落地

根據中國《文匯報》的報導,電動車大廠特斯拉(TESLA)上海子公司在 10 月 17 日下午,成功取得上海臨港裝備產業區總面積達到 864,885 平方公尺的 Q01-05 地塊工業用地,並與上海市規劃和國土資源管理局正式簽訂土地出讓契約,使得特斯拉中國超級工廠(Gigafactory 3)正式在上海落地。

報導指出,根據上海土地市場官網表示,特斯拉上海子公司是以 9.73 億人民幣 (約新台幣 44 億元) 的價格,取得上海臨港裝備產業區 864,885 平方公尺工業用地,象徵著特斯拉中國上海超級工廠將在當地正式落地。

另外,根據《彭博社》的報導,特斯拉已經就中國上海臨港工廠的土地與上海市規劃和國土資源管理局簽訂出讓協議。未來將加快在當地的工廠建設,在這 86 萬平方公尺的土地設立中國超級工廠。根據之前所傳出的消息,特斯拉預計在該超級工廠建立完成後,在此生產每年最高 50 萬輛電動車,以供應全世界市場的需求,只是要達到最高每年 50 萬輛電動車的產能,還要等工廠建立完成至少兩年後才能達成。

值得一提的是,從 7 月 10 日特斯拉與上海臨港管委會、臨港集團簽署純電動車計畫投資協議,到 10 月 17 日土地正式取得,特斯拉只花了約 3 個月的時間。也就是 2018 年 7 月份,特斯拉正式確認將在此臨港地區獨資建設集研發、製造、銷售等功能於一體的特斯拉超級工廠(Gigafactory 3)。而這一預計總金額將可能達到 20 億美元,是上海有史以來最大的外資製造業投資計畫。

另外,根據特斯拉所公布的資料,作為特斯拉旗下目前售價最低和關注度最高的車型,Model 3 的產量和交付量在 2018 年都有明顯增加,前 3 季的產量和交付量均超過了 5 萬輛,使得該款電動汽車的產量和交付量中在所有產品中的比重超過了 60%。

而就在 Model 3 產量提升的同時,特斯拉也沒有忽視更早推出的 Model S 和 Model X 車款。從其公佈的資料來看,這兩款電動汽車在 2018 年前 3 季已交付 71,760 輛,使得這兩款型在 2018 年全年 10 萬輛的交付量目標將有望達成。

(合作媒體:。首圖來源:)

本站聲明:網站內容來源於EnergyTrend https://www.energytrend.com.tw/ev/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※帶您來了解什麼是 USB CONNECTOR  ?

※自行創業缺乏曝光? 網頁設計幫您第一時間規劃公司的形象門面

※如何讓商品強力曝光呢? 網頁設計公司幫您建置最吸引人的網站,提高曝光率!

※綠能、環保無空污,成為電動車最新代名詞,目前市場使用率逐漸普及化

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

※教你寫出一流的銷售文案?

具備效率高壽命長優勢,超級電容可在油電混合車大放異彩

目前全電動車充電少說需要 20-30 分鐘,顯然是個不短的時間,若沒電總不能一直停在路邊充電。因此在大規模採用全電動車之前,油電混合車還是電動車市場的主流,在傳統汽車中新添蓄電池及電動機兩種動力來源,而隨著技術的更迭,現在也有研究員認為若以超級電容取代現有電池,或許可進一步加速混合動力與電動車的發展。

油電混合車可說是傳統燃油車邁向全電動車的重要進展,擁有傳統燃油內燃機與電池電動馬達兩種驅動系統,電池可直接推動車輛,同時回收剩餘的動能為電池充電;傳統內燃機能則可直接產生機械動力推動車輛,或是當成發電機推動電動機或為電池充電。在長距離下就不用補充燃油,能以電動車模式行駛,油耗量與碳排放量不僅都比傳統的汽車還要少,也有助於城市達成節能減碳目標。

其中電池儲能技術在電動車與油電混合車中居關鍵地位,對此美國奧克拉荷馬基督大學 研究員 Surya Sita krishnam raju Alluru 提出一項解決方案,認為技術與發展日益成熟的超級電容可為電動車帶來新的未來。

超級電容具有高效儲能特性,是介於傳統電容與電池之間的雙電層電容,與鋰電池相比不需將電能轉成化學能,可直接以物理方式儲存電能,具有高功率密度、壽命長、充放電效率高、高電壓、耐高溫等優點,有望為提供車輛瞬間功率輸出與提升性能。

該研究員認為超級電容的快充為主要優勢,不管是與電池結合還是獨立使用都對車輛大有裨益,不僅能為電池供電、也可以和內燃機一起提供驅動力,將可提高油電混合車的功率控制與效率,行駛距離也可以進一步提升,且超級電容除了能結合電容與電池提升電路穩定性,也可以調節油電混合車的耗油量。

通常電動車與油電混合車都有搭載動能回收系統(KERS),可將煞車時的動能回收轉為電能,當能量累積到一定程度後駕駛就能利用電力加速推動引擎轉速,藉此獲得更大的動力,而超級電容可更有效的儲存電能。

過去研究也指出,由於超級電容可提供車輛瞬間高功率控制需求,進而提高電池儲能系統的緩衝保護,避免電池過度放電。目前也有不少油電混合車已使用超級電容來取代電池,不僅充電快速,還能可以回收更多煞車能量,並解決電池壽命問題,只不過超級電容目前能量密度仍不高,儲存容量還是比較低,在大規模採用之前,還得進一步證明其性能與優勢。

(首圖來源: CC BY-SA 2.0。文/DaisyChuang)

本站聲明:網站內容來源於EnergyTrend https://www.energytrend.com.tw/ev/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※為什麼 USB CONNECTOR 是電子產業重要的元件?

※網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

※台北網頁設計公司全省服務真心推薦

※想知道最厲害的網頁設計公司"嚨底家"!

※推薦評價好的iphone維修中心